
    i!                     
   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
mZ ddlmZmZ ej                  j!                  dd      Zej                  j!                  dd      ZddlZ ee      j*                  j*                  Ze	j.                  j1                  d ee             ddlZed	z  Zej:                  j=                  d
e      ZdZ ee uZ!e!s ejD                  de!fdee f      d ejF                         v s ejH                  e      r ejJ                  e      nd ejJ                  e       dz  Z&dde&iz  Z' e( ejR                  e'            dxZ!Z ej:                  jU                  e      Z+ejX                  Z!dZ-e!e-uZ.e.s ejD                  de.fde!e-f      d ejF                         v s ejH                  e      r ejJ                  e      nd ejJ                  e!       ejJ                  e-      dz  Z'dde'iz  Z/ e( ejR                  e/            dxZ!xZ.Z-ejX                  ja                  e+        G d d      Z1 G d d      Z2 G d d      Z3 G d d      Z4 G d d      Z5 G d d       Z6 G d! d"      Z7y)#ul  
test_notify_completion.py

scripts/notify-completion.py 단위 테스트 (task-902.1 반영)

테스트 항목:
- argparse 파싱 정상 동작
- send_telegram_notification 기반 완료 통보 검증 (requests.post 방식)
- subprocess 호출 mock (실제 외부 전송 방지)
- 셸 인젝션 방지 (shell=False 패치)
- .done.notified 마커 생성 (O_EXCL)
    N)Path)	MagicMockpatchCOKACDIR_CHAT_ID
6937032012WORKSPACE_ROOTz/home/jay/workspacenotify-completion.pynotify_completion)is not)z%(py0)s is not %(py3)sspecpy0py3assert %(py5)spy5)z2%(py2)s
{%(py2)s = %(py0)s.loader
} is not %(py5)sr   py2r   assert %(py7)spy7c                       e Zd ZdZd Zy)TestArgparseu   argparse 파싱 테스트c                 f   t        j                  t              5 }t        t        j
                  d      rt        j
                  j                  nd ddl}|j                  d      }|j                  d       |j                  dt               |j                  d	d       |j                  d
g       ddd       j                  }|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&                  |      t        j&                  |      dz  }dd|iz  }	t)        t        j*                  |	            dx}x}x}}y# 1 sw Y   xY w)u   --help 플래그 정상 동작__wrapped__Nr   u   팀장 → 아누 완료 통보)descriptiontask_id	--chat-id)defaultz	--anu-keyz--help==)zG%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.value
}.code
} == %(py7)sexc_info)r   r   py4r   zassert %(py9)spy9)pytestraises
SystemExithasattrr
   mainr   argparseArgumentParseradd_argument_TEST_CHAT_ID
parse_argsvaluecode
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanation)
selfr    r(   parser@py_assert1@py_assert3@py_assert6@py_assert5@py_format8@py_format10s
             ;/home/jay/workspace/scripts/tests/test_notify_completion.pytest_help_flagzTestArgparse.test_help_flag+   s-   ]]:& 	*(29:K:P:PR_2`""..fj,,9Z,[F	*]CT:xj)	* ~~'~""'a'"a''''"a''''''x'''x'''~'''"'''a'''''''	* 	*s   BF''F0N)__name__
__module____qualname____doc__r@        r?   r   r   (   s
    #(rF   r   c                   L    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
d	 Zd
 Zd Zy)"TestSendTelegramNotificationDirectuB   send_telegram_notification (requests.post 방식) 직접 테스트c                    t               }d|_        t        j                  dddi      5  t        d|      5 }t        j                  dd      }d	d	d	       d	d	d	       d
}|u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}j                          |j                  }d}	t!        |      }
|	|
v }|st        j                  d|fd|	|
f      t        j                  |	      dt        j                         v st        j                  t               rt        j                  t               nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |
      dz  }dd|iz  }t        t        j                  |            d	x}	x}}
d}	t!        |      }
|	|
v }|st        j                  d|fd|	|
f      t        j                  |	      dt        j                         v st        j                  t               rt        j                  t               nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |
      dz  }dd|iz  }t        t        j                  |            d	x}	x}}
y	# 1 sw Y   xY w# 1 sw Y   xY w)u   200 응답 시 True 반환   
os.environANU_BOT_TOKEN
test-tokenrequests.postreturn_valuechat123   테스트 메시지NTisz%(py0)s is %(py3)sresultr   r   r   in)z0%(py1)s in %(py6)s
{%(py6)s = %(py3)s(%(py4)s)
}strcall_kwargs)py1r   r!   py6assert %(py8)spy8)r   status_coder   dictr
   send_telegram_notificationr/   r0   r1   r2   r3   r4   r5   r6   assert_called_once	call_argsrY   )r7   	mock_resp	mock_postrV   @py_assert2r9   @py_format4@py_format6rZ   @py_assert0r<   @py_format7@py_format9s                r?   test_success_returns_truez<TestSendTelegramNotificationDirect.test_success_returns_true<   s	   K	 #	ZZ&EF 	hY? h9*EEiQfgh	h v~vvv$$&)),C,,y,,,,,y,,,,y,,,,,,C,,,C,,,,,,,,,,,,,,,,,,,,$8K(88$(88888$(8888$888888888888888K888K888(88888888h h	h 	hs"   ML?M?M		MMc                 N   t         j                  j                         D ci c]  \  }}|dk7  s|| }}}t        j                  d|d      5  t
        j                  dd      }ddd       d}|u }|st        j                  d	|fd
||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}yc c}}w # 1 sw Y   xY w)u(   ANU_BOT_TOKEN 미설정 시 False 반환rL   rK   TclearrQ   	   메시지NFrS   rU   rV   r   r   r   )osenvironitemsr   r`   r
   ra   r/   r0   r1   r2   r3   r4   r5   r6   )	r7   kvenvrV   rf   r9   rg   rh   s	            r?   test_no_bot_token_returns_falsezBTestSendTelegramNotificationDirect.test_no_bot_token_returns_falseK   s     "

 0 0 2K1a?6Jq!tKKZZc6 	Z&AA)[YF	Zvvvv L	Z 	Zs   DDDD$c                 H   t               }d|_        i |_        t        j                  dddi      5  t        d|      5  t
        j                  dd      }d	d	d	       d	d	d	       d
}|u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}y	# 1 sw Y   xY w# 1 sw Y   xY w)u.   200이 아닌 응답(400 등) 시 False 반환  rK   rL   rM   rN   rO   rQ   rp   NFrS   rU   rV   r   r   r   )r   r_   headersr   r`   r
   ra   r/   r0   r1   r2   r3   r4   r5   r6   )r7   rd   rV   rf   r9   rg   rh   s          r?   test_non_200_returns_falsez=TestSendTelegramNotificationDirect.test_non_200_returns_falseR   s    K	 #		ZZ&EF 	^Y? ^*EEiQ\]^	^ vvvv^ ^	^ 	^s"   DDDD	DD!c                 P   ddl }t               }d|_        ddi|_        t               }d|_        t	        j
                  ddd	i      5  t	        d
||g      5 }t	        d      5  t        j                  dd      }ddd       ddd       ddd       d}|u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}}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  }	dd|	iz  }t        t        j                  |            dx}x}}
y# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w)u5   429 응답 시 Retry-After 헤더를 따라 재시도r   Ni  zRetry-After1rJ   rK   rL   rM   rN   side_effect
time.sleeprQ   rp   TrS   rU   rV   r   r   r      r   z2%(py2)s
{%(py2)s = %(py0)s.call_count
} == %(py5)sre   r   r   r   )requestsr   r_   rz   r   r`   r
   ra   r/   r0   r1   r2   r3   r4   r5   r6   
call_count)r7   real_requestsmock_429mock_200re   rV   rf   r9   rg   rh   @py_assert4r:   r=   s                r?   !test_429_retries_with_retry_afterzDTestSendTelegramNotificationDirect.test_429_retries_with_retry_after]   s   (;")3/;"ZZ&EF 	bXx4HI bY<( b.II)U`aFbb	b
 v~vvv##(q(#q((((#q((((((y(((y(((#(((q(((((((	b bb b	b 	bs<   HH$H;HHHHH	HH%c                 ~   ddl }t        j                  dddi      5  t        d|j                  d            5  t        d	      5  t        j                  d
d      }ddd       ddd       ddd       d}|u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w)u2   네트워크 에러 시 재시도 후 False 반환r   NrK   rL   rM   rN   zconnection errorr~   r   rQ   rp   FrS   rU   rV   r   r   r   )r   r   r`   RequestExceptionr
   ra   r/   r0   r1   r2   r3   r4   r5   r6   )r7   r   rV   rf   r9   rg   rh   s          r?   test_request_exception_retrieszATestSendTelegramNotificationDirect.test_request_exception_retriesp   s    (ZZ&EF 	bM4R4RSe4fg b<( b.II)U`aFbb	b vvvvb bb b	b 	bs:   D3D'DD'%D3D$ D''D0	,D33D<c                    t               }d|_        t        j                  dddi      5  t        d|      5 }t        j                  dd       d	d	d	       d	d	d	       j                  }|d
   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  }dd|iz  }t        t        j                  |            d	x}}d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                  |            d	x}}d}||v }|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                  |            d	x}}y	# 1 sw Y   xY w# 1 sw Y   xY w)u!   올바른 Telegram API URL 사용rJ   rK   rL   
mytoken123rN   rO   rQ   rp   Nr   rW   z%(py1)s in %(py3)surlr[   r   r   r   zapi.telegram.orgsendMessage)r   r_   r   r`   r
   ra   rc   r/   r0   r4   r1   r2   r3   r5   r6   )	r7   rd   re   rc   r   ri   rf   rg   rh   s	            r?   test_uses_correct_api_urlz<TestSendTelegramNotificationDirect.test_uses_correct_api_urlz   s   K	 #	ZZ&EF 	UY? U9!<<YTU	U ''	l1o"|s""""|s"""|""""""s"""s"""""""!(!S((((!S(((!((((((S(((S(((((((#}####}###}################U U	U 	Us"   I0I#I0#I-	(I00I:c                    t               }d|_        t        j                  dddi      5  t        d|      5 }t        j                  dd       d	d	d	       d	d	d	       j                  d
   }|j                  }d} ||      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            d	x}x}x}x}}y	# 1 sw Y    xY w# 1 sw Y   %xY w)u   timeout=10 필수rJ   rK   rL   rM   rN   rO   rQ   rp   N   timeout
   r   zI%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
} == %(py9)srZ   r   r   r!   r\   r"   assert %(py11)spy11r   r_   r   r`   r
   ra   rc   getr/   r0   r1   r2   r3   r4   r5   r6   )r7   rd   re   rZ   r9   r:   r<   @py_assert8@py_assert7r>   @py_format12s              r?   test_timeout_is_10_secondsz=TestSendTelegramNotificationDirect.test_timeout_is_10_seconds   s   K	 #	ZZ&EF 	UY? U9!<<YTU	U  ))!,/y/y)/R/)R////)R//////{///{//////y///)///R////////	U U	U 	Us"   E:E-E:-E7	2E::Fc                 2   t               }d|_        t        j                  dddi      5  t        d|      5 }t        j                  dd       d	d	d	       d	d	d	       j                  d
   }|j                  di       }|j                  }d} ||      }d}||k(  }	|	st        j                  d|	fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }
dd|
iz  }t        t        j                  |            d	x}x}x}x}	}y	# 1 sw Y   2xY w# 1 sw Y   7xY w)uJ   requests.post payload에 parse_mode: 'Markdown'이 포함되어야 한다.rJ   rK   rL   rM   rN   rO   rQ   rR   Nr   json
parse_modeMarkdownr   r   payloadr   r   r   r   )r7   rd   re   rZ   r   r9   r:   r<   r   r   r>   r   s               r?   #test_parse_mode_markdown_in_payloadzFTestSendTelegramNotificationDirect.test_parse_mode_markdown_in_payload   s-   K	 #	ZZ&EF 	_Y? _9!<<YH]^_	_  ))!,//&"-{{6<6{<(6J6(J6666(J666666w666w666{666<666(666J66666666_ _	_ 	_s"   FE?F?F		FFc                    t               }d|_        i |_        t               }d|_        t        j                  dddi      5  t        d||g      5 }t
        j                  dd	       d
d
d
       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  }dd|iz  }t        t        j                  |            d
x}x}}|j                   d   d   }	|	j#                  di       }
|
j"                  }d} ||      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |
      rt        j                  |
      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            d
x}x}x}x}}|j                   d   d   }|j#                  di       }d}||v}|st        j                  d|fd||f      t        j                  |      dt        j                         v st        j                  |      rt        j                  |      nddz  }d d!|iz  }t        t        j                  |            d
x}}y
# 1 sw Y   xY w# 1 sw Y   xY w)"u   Markdown 파싱 에러(400) 시 plain text fallback으로 2회 호출되어야 한다.
        첫 번째 호출에는 parse_mode가 있고 두 번째에는 없어야 한다.ry   rJ   rK   rL   rM   rN   r~   rQ   rR   Nr   r   r   re   r   r   r   r   r   r   r   r   r   first_payloadr   r   r   not inz%(py1)s not in %(py3)ssecond_payloadr   r   r   )r   r_   rz   r   r`   r
   ra   r   r/   r0   r1   r2   r3   r4   r5   r6   call_args_listr   )r7   mock_400r   re   r9   r   r:   rh   r=   first_call_kwargsr   r<   r   r   r>   r   second_call_kwargsr   ri   rf   rg   s                        r?   "test_markdown_parse_error_fallbackzETestSendTelegramNotificationDirect.test_markdown_parse_error_fallback   sG    ;";"ZZ&EF 	_Xx4HI _Y!<<YH]^_	_ ##(q(#q((((#q((((((y(((y(((#(((q(((((((%44Q7:)--fb9  << .<*<.*<<<<.*<<<<<<}<<<}<<< <<<<<<.<<<*<<<<<<<&55a8;+//;1|>1111|>111|111111>111>1111111_ _	_ 	_s$   L:L-)L:-L7	2L::Mc                    t               }d|_        i |_        t               }d|_        t        j                  dddi      5  t        d||g      5 }t
        j                  dd	      }d
d
d
       d
d
d
       j                  d   d   j                  di       }|j                  }d} ||      }d}	||	k(  }
|
st        j                  d|
fd||	f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      t        j                  |	      dz  }dd|iz  }t        t        j                   |            d
x}x}x}x}
}	d}|u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                   |            d
x}}y
# 1 sw Y   xY w# 1 sw Y   xY w)u   첫 번째 Markdown 전송이 400일 때 fallback 성공 시 True를 반환해야 한다.
        (parse_mode=Markdown → 400 → parse_mode 없는 재전송 → 200 → True)ry   rJ   rK   rL   rM   rN   r~   rQ   rR   Nr   r   r   r   r   r   r   r   r   r   r   TrS   rU   rV   r   r   r   )r   r_   rz   r   r`   r
   ra   r   r   r/   r0   r1   r2   r3   r4   r5   r6   )r7   r   r   re   rV   r   r9   r:   r<   r   r   r>   r   rf   rg   rh   s                   r?   %test_fallback_returns_true_on_successzHTestSendTelegramNotificationDirect.test_fallback_returns_true_on_success   s    ;";"ZZ&EF 	hXx4HI hY*EEiQfgh	h
 "003A6::62F  << .<*<.*<<<<.*<<<<<<}<<<}<<< <<<<<<.<<<*<<<<<<<v~vvvh h	h 	hs$   IH?)I?I		IIN)rA   rB   rC   rD   rl   rw   r{   r   r   r   r   r   r   r   rE   rF   r?   rH   rH   9   s7    L9	)&$
072,rF   rH   c                   l    e Zd ZdZddZ ed      d        Z ed      d        Z ed      d        Zy)	TestMainWithMocku/   main() 함수 테스트 (requests.post를 mock)Nc                 $    dddddfd}|S )u(   subprocess.run side_effect 공통 생성NFin_chainis_lastchain_idnext_task_idc                 D   t               }d|_        d|_        t        | t              rZdj                  d | D              }d|v r d|v rt        j                        |_        |S t        j                  ddi      |_        |S t        j                  ddi      |_        |S )	Nr     c              3   2   K   | ]  }t        |        y wNrY   .0cs     r?   	<genexpr>zUTestMainWithMock._make_subprocess_side_effect.<locals>.side_effect.<locals>.<genexpr>        "7a3q6"7   chain_manager.pycheckstatusok	r   
returncodestderr
isinstancelistjoinr   dumpsstdout)cmdargskwargsrV   cmd_strchain_results        r?   r   zBTestMainWithMock._make_subprocess_side_effect.<locals>.side_effect   s    [F !FFM#t$(("73"77%0W5G$(JJ|$<FM
 M %)JJ$/?$@FM M !%

Hd+; <MrF   rE   )r7   r   r   s    ` r?   _make_subprocess_side_effectz-TestMainWithMock._make_subprocess_side_effect   s%    (-%TcghL	 rF   subprocess.runc           	         | j                         |_        |dz  dz  j                  dd       t               }d|_        t        dddg      5  t        j                  d	d
dd      5  t        j                  t        dt        |            5  t        d|      5 }t        j                          ddd       ddd       ddd       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  }dd|iz  }	t%        t        j&                  |	            dx}x}}|j(                  }
t+        |
d         dkD  r|
d   j-                  d      xs |
d   d   n|
d   j-                  d      }||
d   r|
d   j-                  d      }|r|j-                  dd      n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"}||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# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w)$uM   main()이 send_telegram_notification(requests.post)를 호출하는지 확인memoryeventsTparentsexist_okrJ   sys.argvr	   
task-999.1rK   rM   test-anu-keyrL   COKACDIR_KEY_ANUr   rN   rO   Nr   >=z2%(py2)s
{%(py2)s = %(py0)s.call_count
} >= %(py5)sre   r   r   r   r   r   textr   rW   r   r   u    메시지에 task_id가 없음: 
>assert %(py5)sr      완료u!   메시지에 '완료'가 없음: )r   r   mkdirr   r_   r   r`   objectr
   rY   r'   r   r/   r0   r1   r2   r3   r4   r5   r6   rc   lenr   _format_assertmsg)r7   mock_runtmp_pathrd   re   r9   r   r:   rh   r=   rc   r   r   ri   rf   rg   s                   r?   test_main_calls_requests_postz.TestMainWithMock.test_main_calls_requests_post   s     $@@B	H	x	'..td.KK	 #	 *5|DE	%JJ||Yg%hi	% LL*,<c(mL	% /	:		% ?H""$	% 	% 	% 	% ##(q(#q((((#q((((((y(((y(((#(((q(((((((''	ADYq\ARUVAV)A,""6*=il1o\efg\h\l\lms\t?y|l&&v.G*1w{{62&rN|t#NNN|tNNN|NNNNNNtNNNtNNNN'Gv%NNNNNNNKx4KKKx4KKKxKKKKKK4KKK4KKKK#DTF!KKKKKKK!	% 	% 	% 	% 	% 	% 	% 	%sT   N<(%N/N"N	0N"8N/ N<NN""N,'N//N9	4N<<Oc           	      
   | j                         |_        |dz  dz  j                  dd       t               }d|_        t        dg d      5  t        j                  dd	d
d      5  t        j                  t        dt        |            5  t        d|      5 }t        j                          ddd       ddd       ddd       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  }dd|iz  }	t%        t        j&                  |	            dx}x}}|j(                  d   }
|
j+                  di       }|j*                  }d} ||      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                   |      rt        j"                  |      ndt        j"                  |      t        j"                  |      t        j"                  |      t        j"                  |      dz  }t        j,                  d|       dz   d |iz  }t%        t        j&                  |            dx}x}x}x}}y# 1 sw Y   #xY w# 1 sw Y   (xY w# 1 sw Y   -xY w# 1 sw Y   2xY w)!uB   --chat-id 커스텀 값 전달 시 requests.post payload에 반영r   r   Tr   rJ   r   )r	   ztask-1.1r   12345rK   rM   r   r   r   rN   rO   Nr   r   r   re   r   r   r   r   chat_idr   r   r   r   r   u   chat_id가 12345가 아님: z
>assert %(py11)sr   )r   r   r   r   r_   r   r`   r   r
   rY   r'   r   r/   r0   r1   r2   r3   r4   r5   r6   rc   r   r   )r7   r   r   rd   re   r9   r   r:   rh   r=   rZ   r   r<   r   r   r>   r   s                    r?   test_main_custom_chat_idz)TestMainWithMock.test_main_custom_chat_id   s*     $@@B	H	x	'..td.KK	 #	 *XY	%JJ||Yg%hi	% LL*,<c(mL	% /	:		% ?H""$	% 	% 	% 	% ##(q(#q((((#q((((((y(((y(((#(((q((((((())!,//&"-{{Z9Z{9%ZZ%0ZZZ%ZZZZZZwZZZwZZZ{ZZZ9ZZZ%ZZZZZZ4PQXPY2ZZZZZZZZ	% 	% 	% 	% 	% 	% 	% 	%sT   K8(%K+KK	0K8K+ K8KKK(#K++K5	0K88Lc           	         | j                         |_        |dz  dz  j                  dd       t               }d|_        t        dddg      5  t        j                  d	d
dd      5  t        j                  t        dt        |            5  t        d|      5  t        j                          ddd       ddd       ddd       ddd       |j                  D cg c]*  }t        |d   d   t              s|d   d   d   dk(  s)|, }}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# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY wc c}w )u?   main() 실행 시 cokacdir 명령이 호출되지 않아야 함r   r   Tr   rJ   r   r	   r   rK   rM   r   r   r   rN   rO   Nr   cokacdirr   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)sr   cokacdir_callsr   r[   r   r\   u!   cokacdir 호출이 남아있음: 
>assert %(py8)sr^   )r   r   r   r   r_   r   r`   r   r
   rY   r'   r   r   r   r   r/   r0   r1   r2   r3   r4   r   r5   r6   )r7   r   r   rd   r   r   rf   r<   r   rj   rk   s              r?   test_main_no_cokacdir_calledz-TestMainWithMock.test_main_no_cokacdir_called  s     $@@B	H	x	'..td.KK	 #	 *5|DE	%JJ||Yg%hi	% LL*,<c(mL	% /	:		% ""$	% 	% 	% 	% &.%<%<w
1Q4PQ7TX@Y^_`a^bcd^efg^hlv^v!ww>"]a]"a']]]"a]]]]]]s]]]s]]]]]]>]]]>]]]"]]]a]]]+L^L\)]]]]]]]]	% 	% 	% 	% 	% 	% 	% 	% xsf   I!(%IIH:	0I8I I!I.:I.	I.:I?IIII	I!!I+r   )	rA   rB   rC   rD   r   r   r   r   r   rE   rF   r?   r   r      s^    9* L L6 [ [, ^ ^rF   r   c                   d    e Zd ZdZ ed      d        Z ed      d        Z ed      d        Zy)TestDoneNotifiedMarkeru-   .done.notified 마커 파일 생성 테스트r   c           	      x   d }||_         |dz  dz  }|j                  dd       t               }d|_        t	        ddd	g      5  t	        j
                  d
ddd      5  t	        j                  t        dt        |            5  t	        d|      5  t        j                          ddd       ddd       ddd       ddd       |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}}y# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w)u5   알림 성공 시 .done.notified 마커 파일 생성c                 L   t               }d|_        d|_        t        | t              r_dj                  d | D              }d|v r%d|v r!t        j                  ddd d d      |_        |S t        j                  d	d
i      |_        |S t        j                  d	d
i      |_        |S )Nr   r   r   c              3   2   K   | ]  }t        |        y wr   r   r   s     r?   r   zkTestDoneNotifiedMarker.test_done_notified_marker_created_on_success.<locals>.side_effect.<locals>.<genexpr>:  r   r   r   r   Fr   r   r   r   r   r   r   rV   r   s        r?   r   zXTestDoneNotifiedMarker.test_done_notified_marker_created_on_success.<locals>.side_effect5      [F !FFM#t$(("73"77%0W5G$(JJ%*u$`de%FM M %)JJ$/?$@FM M !%

Hd+; <MrF   r   r   Tr   rJ   r   r	   z
task-777.1rK   rM   r   r   r   rN   rO   Nztask-777.1.done.notifiedu.   .done.notified 마커가 생성되지 않음: C
>assert %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}notified_pathr   r   r!   )r   r   r   r_   r   r`   r   r
   rY   r'   existsr/   r   r1   r2   r3   r4   r5   r6   )
r7   r   r   r   
events_dirrd   r  r9   r:   @py_format5s
             r?   ,test_done_notified_marker_created_on_successzCTestDoneNotifiedMarker.test_done_notified_marker_created_on_success1  sn   	   +(83
5K	 #	 *5|DE	%JJ||Yg%hi	% LL*,<c(mL	% /	:		% ""$	% 	% 	% 	% #%??##g#%g%gg)WXeWf'ggggggg}ggg}ggg#ggg%gggggg	% 	% 	% 	% 	% 	% 	% 	%sT   F0%F$FF	'F/F$7F0FFF!F$$F-	)F00F9c           	         d }||_         |dz  dz  }|j                  dd       t        j                  j	                         D ci c]  \  }}|dk7  s|| }}}d|d<   t        d	d
dg      5  t        j                  d|d      5  t        j                  t        dt        |            5  t        j                          ddd       ddd       ddd       |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}
}yc c}}w # 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w)u=   알림 실패 시 .done.notified 마커 파일 생성 안 함c                 L   t               }d|_        d|_        t        | t              r_dj                  d | D              }d|v r%d|v r!t        j                  ddd d d      |_        |S t        j                  d	d
i      |_        |S t        j                  d	d
i      |_        |S )Nr   r   r   c              3   2   K   | ]  }t        |        y wr   r   r   s     r?   r   zoTestDoneNotifiedMarker.test_done_notified_marker_not_created_on_failure.<locals>.side_effect.<locals>.<genexpr>a  r   r   r   r   Fr   r   r   r   r  s        r?   r   z\TestDoneNotifiedMarker.test_done_notified_marker_not_created_on_failure.<locals>.side_effect\  r  rF   r   r   Tr   rL   r   r   r   r	   z
task-888.1rK   rn   r   Nztask-888.1.done.notifiedu4   .done.notified 마커가 불필요하게 생성됨: zG
>assert not %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.exists
}()
}r  r  )r   r   rq   rr   rs   r   r`   r   r
   rY   r'   r	  r/   r   r1   r2   r3   r4   r5   r6   )r7   r   r   r   r
  rt   ru   rv   r  r9   r:   r<   rh   s                r?   0test_done_notified_marker_not_created_on_failurezGTestDoneNotifiedMarker.test_done_notified_marker_not_created_on_failureX  s   	   +(83
5 !#

 0 0 2K1a?6Jq!tKK"0*5|DE	%JJ|S5	% LL*,<c(mL	%
 ""$	% 	% 	% #%?? ''q')q))q)qq-aboap+qqqqqqq=qqq=qqq'qqq)qqqqqq L	% 	% 	% 	% 	% 	%sH   F%F%0G	%F7.F+F7G+F40F77G 	<GGc           	         d }||_         |dz  dz  }|j                  dd       |dz  }|j                          t               }d|_        t        dd	d
g      5  t        j                  dddd      5  t        j                  t        dt        |            5  t        d|      5  t        j                          ddd       ddd       ddd       ddd       y# 1 sw Y   "xY w# 1 sw Y   &xY w# 1 sw Y   *xY w# 1 sw Y   yxY w)uH   이미 마커 존재 시 중복 생성 시도해도 에러 없이 처리c                 L   t               }d|_        d|_        t        | t              r_dj                  d | D              }d|v r%d|v r!t        j                  ddd d d      |_        |S t        j                  d	d
i      |_        |S t        j                  d	d
i      |_        |S )Nr   r   r   c              3   2   K   | ]  }t        |        y wr   r   r   s     r?   r   zkTestDoneNotifiedMarker.test_done_notified_marker_oexcl_no_duplicate.<locals>.side_effect.<locals>.<genexpr>  r   r   r   r   Fr   r   r   r   r  s        r?   r   zXTestDoneNotifiedMarker.test_done_notified_marker_oexcl_no_duplicate.<locals>.side_effect  r  rF   r   r   Tr   ztask-555.1.done.notifiedrJ   r   r	   z
task-555.1rK   rM   r   r   r   rN   rO   N)r   r   touchr   r_   r   r`   r   r
   rY   r'   )r7   r   r   r   r
  r  rd   s          r?   ,test_done_notified_marker_oexcl_no_duplicatezCTestDoneNotifiedMarker.test_done_notified_marker_oexcl_no_duplicate~  s   	   +(83
5 #%??K	 #	 *5|DE	%JJ||Yg%hi	% LL*,<c(mL	% /	:		% ""$	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	%sT   D4%C5C)'C	<C)C5DC&"C))C2.C55C>	:DD
N)rA   rB   rC   rD   r   r  r  r  rE   rF   r?   r   r   .  sW    7
$h $hL #r #rJ &% &%rF   r   c                   "    e Zd ZdZd Zd Zd Zy)TestDispatchNextPhaseWithTaskIdui   dispatch_next_phase 결과에서 task_id를 받아 dispatch 명령에 --task-id를 추가하는 테스트c                    dddddd}|j                  d      }|j                  d      }|j                  d	d
      }|j                  d      }t        }|rd| nd}d| d| d| d| 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}	|	|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)ug   dispatch_next_phase 결과에 task_id가 있으면 dispatch 명령에 --task-id 인자가 추가된다.dispatchmemory/tasks/task-566.2.md	dev1-team
scoped-566
task-566.2action	task_fileteamr   r   r   r!  levelnormalr    --task-id r   source /.env.keys && python3 /dispatch.py --team  --task-file 	 --level 	--task-idrW   r   dispatch_cmdr   (   dispatch 명령에 --task-id가 없음: r   r   N)   dispatch 명령에 task-566.2가 없음: r   
_WORKSPACEr/   r0   r4   r1   r2   r3   r   r5   r6   r7   dispatch_resultr   r!  r"  r   workspace_roottask_id_argr+  ri   rf   rg   rh   s                r?   ,test_dispatch_next_phase_result_with_task_idzLTestDispatchNextPhaseWithTaskId.test_dispatch_next_phase_result_with_task_id  s    !5$#
 $''4	""6*##GX6&**95#6BL>2n%%;N;K LV=9UGK=R 	
 e{l*eee{leee{eeeeeeleeeleeee.VWcVd,eeeeeeeg||+ggg||ggg|gggggg|ggg|gggg/XYeXf-gggggggrF   c                 \   ddddd}|j                  d      }|j                  d      }|j                  dd	      }|j                  d
      }t        }|rd| nd}d| d| d| d| 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}	}
y)uZ   dispatch_next_phase 결과에 task_id가 없으면 dispatch 명령에 --task-id가 없다.r  zmemory/tasks/task-567.2.mdr  z
scoped-567)r  r   r!  r   r   r!  r"  r#  r   r$  r   r%  r&  r'  r(  r)  r*  r   r   r+  r   u'   task_id 없는데 --task-id가 있음: r   r   Nr.  r0  s                r?   /test_dispatch_next_phase_result_without_task_idzOTestDispatchNextPhaseWithTaskId.test_dispatch_next_phase_result_without_task_id  s    !5$	
 $''4	""6*##GX6&**95#6BL>2n%%;N;K LV=9UGK=R 	
 h{,.hhh{,hhh{hhhhhh,hhh,hhhh2YZfYg0hhhhhhhrF   c           
      &   ddl }d |dz  dz  }|j                  dd       g fd}t               }d	|_        t	        j
                  t        d
      5 }t	        j
                  t        dt        |            5  t	        dddg      5  t	        j                  dddd      5  t	        d|      5  ||j                  _
        |j                  |_        t        j                          ddd       ddd       ddd       ddd       dd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}	}dj/                  d d   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(}||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# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY w)*uh   실제 notify_completion의 dispatch 호출이 --task-id를 포함하는지 확인 (shell=False 방식).r   Nc                    t               }d|_        t        | t              rdj	                  d | D              }d|v r$d|v r t        j                  dddd	d
      |_        nd|v r%d|v r!t        j                  ddddd	d      |_        n[d|v rt        j                  dd	d      |_        n9t        j                  ddi      |_        nt        j                  ddi      |_        d|_        |S )Nr   r   c              3   2   K   | ]  }t        |        y wr   r   r   s     r?   r   zrTestDispatchNextPhaseWithTaskId.test_notify_completion_dispatch_cmd_format.<locals>.side_effect.<locals>.<genexpr>  r   r   r   r   TFr  r  r   nextr  r  r  r  dispatch.py
dispatched)r   r   r   r   r   )	r   r   r   r   r   r   r   r   r   r  s        r?   r   z_TestDispatchNextPhaseWithTaskId.test_notify_completion_dispatch_cmd_format.<locals>.side_effect  s    [F !F#t$(("73"77%0W5G$(JJ%)egst%FM (72v7H$(JJ&0)E$/(4'3%FM #g-$(JJ,S_/`$aFM$(JJ$/?$@FM $

Hd+; <FMMrF   r   r   Tr   c                     t        | t              r+ddj                  d | D              v rj                  |         | g|i |S )Nr;  r   c              3   2   K   | ]  }t        |        y wr   r   r   s     r?   r   z|TestDispatchNextPhaseWithTaskId.test_notify_completion_dispatch_cmd_format.<locals>.capturing_side_effect.<locals>.<genexpr>  s     BWa3q6BWr   )r   r   r   append)r   r   r   dispatch_callsr   s      r?   capturing_side_effectziTestDispatchNextPhaseWithTaskId.test_notify_completion_dispatch_cmd_format.<locals>.capturing_side_effect  sG    #t$#((BWSVBW:W)W%%c*s4T4V44rF   rJ   
subprocessr   r   r	   z
task-568.1rK   rM   r   r   rN   rO   r   r   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} >= %(py6)sr   r@  r   u   dispatch.py 호출이 없음r   r^   r   c              3   2   K   | ]  }t        |        y wr   r   r   xs     r?   r   z]TestDispatchNextPhaseWithTaskId.test_notify_completion_dispatch_cmd_format.<locals>.<genexpr>  s     $GSV$Gr   r*  rW   r   dispatch_cmd_fullr   r,  r   r   r  r-  )rB  r   r   r_   r   r   r
   rY   r`   runr   TimeoutExpiredr'   r   r/   r0   r1   r2   r3   r4   r   r5   r6   r   )r7   r   real_subprocessr
  rA  rd   mock_subrf   r<   r   rj   rk   rG  ri   rg   rh   r@  r   s                   @@r?   *test_notify_completion_dispatch_cmd_formatzJTestDispatchNextPhaseWithTaskId.test_notify_completion_dispatch_cmd_format  s   ,	: (83
5!	5
 K	 #	 LL*L9		%=ELL*,<c(mL		% *5|DE		% JJ||Yg%hi			%
 /	:		% (=HLL$&5&D&DH#""$		% 		% 		% 		% 		% >"GaG"a'GGG"aGGGGGGsGGGsGGGGGG>GGG>GGG"GGGaGGG)GGGGGGGGHH$G^A5F$GGo{//ooo{/ooo{oooooo/ooo/oooo3[\m[n1oooooooq|00qqq|0qqq|qqqqqq0qqq0qqqq4]^o]p2qqqqqqq		% 		% 		% 		% 		% 		% 		% 		% 		% 		%sl   %P=O9O,&O	47O+O	3O,;O9POO	O)$O,,O61O99P	>PPN)rA   rB   rC   rD   r4  r6  rL  rE   rF   r?   r  r    s    sh0i.=rrF   r  c                   R    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
d	 Zd
 Zd Zd Zy)TestShellInjectionPreventionu<   셸 인젝션 방지 테스트 (Fenrir P0 위협 1-B 대응)c           
      :   ddl }d g fd}t               }d|_        t        j                  t
        d      5 }t        j                  t
        dt        |            5  t        dd	d
g      5  t        j                  dddd      5  t        d|      5  ||j                  _	        |j                  |_
        t
        j                          ddd       ddd       ddd       ddd       ddd       D cg c]6  }t        |t              st        |      dk\  s#|d   dk(  s,|d   dk(  s5|8 }}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}
}	D cg c]<  }t        |t              r*t        |      dk\  r|d   dk(  rt1        d |D              r|> }}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# 1 sw Y   xY w# 1 sw Y   	xY w# 1 sw Y   xY w# 1 sw Y   xY w# 1 sw Y   xY wc c}w c c}w )#u\   dispatch 호출이 ['bash', '-c', ...] 대신 ['python3', ...] 리스트 방식인지 확인r   Nc                    t               }d|_        d|_        t        | t              rdj                  d | D              }d|v r%d|v r!t        j                  ddd	d
d      |_        |S d|v r&d|v r"t        j                  ddddd
d      |_        |S t        j                  ddi      |_        |S t        j                  ddi      |_        |S )Nr   r   r   c              3   2   K   | ]  }t        |        y wr   r   r   s     r?   r   zgTestShellInjectionPrevention.test_dispatch_uses_list_not_bash_c.<locals>.side_effect.<locals>.<genexpr>&  r   r   r   r   TFz
scoped-100z
task-100.2r   r:  r  zmemory/tasks/task-100.2.mdr  r#  )r  r   r!  r"  r   r   r   r   r  s        r?   r   zTTestShellInjectionPrevention.test_dispatch_uses_list_not_bash_c.<locals>.side_effect!  s    [F !FFM#t$(("73"77%0W5G$(JJ(,',(4,8	%FM, M (72v7H$(JJ&0)E$/%-'3%FM M %)JJ$/?$@FM M !%

Hd+; <MrF   c                 >    j                  |         | g|i |S r   )r?  )r   r   r   	all_callsr   s      r?   rA  z^TestShellInjectionPrevention.test_dispatch_uses_list_not_bash_c.<locals>.capturing_side_effectB  s&    S!s4T4V44rF   rJ   rB  r   r   r	   
task-100.1rK   rM   r   r   rN   rO   r   bashr   z-cr   r   r   bash_c_callsr   u7   bash -c 호출이 남아있음 (셸 인젝션 위험): r   r^   python3c              3   6   K   | ]  }d t        |      v   yw)r;  Nr   rE  s     r?   r   zRTestShellInjectionPrevention.test_dispatch_uses_list_not_bash_c.<locals>.<genexpr>\  s     PtmnQ^befgbhQhPts   r   rC  python3_dispatch_callsu.   python3 dispatch.py 리스트 호출이 없음)rB  r   r_   r   r   r
   rY   r`   rH  r   rI  r'   r   r   r   r/   r0   r1   r2   r3   r4   r   r5   r6   any)r7   r   rJ  rA  rd   rK  r   rV  rf   r<   r   rj   rk   rY  rS  r   s                 @@r?   "test_dispatch_uses_list_not_bash_cz?TestShellInjectionPrevention.test_dispatch_uses_list_not_bash_c  s   ,	> 		5 K	 #	 LL*L9		%=ELL*,<c(mL		% *5|DE		% JJ||Yg%hi			%
 /	:		% (=HLL$&5&D&DH#""$		% 		% 		% 		% 		% $-xa
1d0CARSXYZ[X\`fXfklmnkoswkwxx< oAo A%ooo Aoooooosooosoooooo<ooo<ooo oooAooo)`am`n'oooooooo
 "
!T"s1v{qty7HSPtrsPtMt "
 "

 )*aaa*a/aaa*aaaaaaasaaasaaaaaa)aaa)aaa*aaaaaaa1aaaaaaaa+		% 		% 		% 		% 		% 		% 		% 		% 		% 		% y"
s   %P"O91O,O	7OO	O, O9(P<PP!P*P3P'APOO	O)$O,,O61O99P	>PPc                     t        j                  t              5  t        j	                  dddd       ddd       y# 1 sw Y   yxY w)uB   team에 셸 메타문자(;rm -rf /)가 있으면 ValueError 발생z	;rm -rf /memory/tasks/task-1.1.mdr#  Nr!  r   r"  r   r#   r$   
ValueErrorr
   _validate_dispatch_argsr7   s    r?   2test_validate_dispatch_args_rejects_shell_metacharzOTestShellInjectionPrevention.test_validate_dispatch_args_rejects_shell_metachar`  B    ]]:& 	55 4!	 6 	 	 		   =Ac                     t        j                  t              5  t        j	                  dddd       ddd       y# 1 sw Y   yxY w)u<   task_file에 셸 메타문자가 있으면 ValueError 발생r  z$memory/tasks/task-1.1.md; echo pwnedr#  Nr^  r_  rb  s    r?   6test_validate_dispatch_args_rejects_task_file_metacharzSTestShellInjectionPrevention.test_validate_dispatch_args_rejects_task_file_metacharj  sB    ]]:& 	55 @!	 6 	 	 	re  c                     t        j                  t              5  t        j	                  dddd       ddd       y# 1 sw Y   yxY w)uH   level이 허용값(normal/critical/security) 외이면 ValueError 발생r  r]  HACKEDNr^  r_  rb  s    r?   1test_validate_dispatch_args_rejects_invalid_levelzNTestShellInjectionPrevention.test_validate_dispatch_args_rejects_invalid_levelt  rd  re  c                     t        j                  t              5  t        j	                  dddd       ddd       y# 1 sw Y   yxY w)uH   next_task_id가 task-숫자.숫자 형식이 아니면 ValueError 발생r  r]  r#  ztask-1.1; drop table tasksr^  Nr_  rb  s    r?   8test_validate_dispatch_args_rejects_invalid_next_task_idzUTestShellInjectionPrevention.test_validate_dispatch_args_rejects_invalid_next_task_id~  sB    ]]:& 	55 49	 6 	 	 	re  c                 6    t         j                  dddd       y)u.   정상적인 인자값은 예외 없이 통과r  r  r#  r  r^  Nr
   ra  rb  s    r?   )test_validate_dispatch_args_accepts_validzFTestShellInjectionPrevention.test_validate_dispatch_args_accepts_valid  s!    112%	 	2 	
rF   c                 6    t         j                  dddd       y)u"   next_task_id=None은 정상 통과r  r  criticalNr^  rn  rb  s    r?   4test_validate_dispatch_args_accepts_valid_no_task_idzQTestShellInjectionPrevention.test_validate_dispatch_args_accepts_valid_no_task_id  s!    112	 	2 	
rF   c                 D    t         j                  dt         ddd       y)u   level=security도 정상 통과z
alpha-teamz/memory/tasks/task-1.1.mdsecurityztask-1.2r^  N)r
   ra  r/  rb  s    r?   2test_validate_dispatch_args_accepts_security_levelzOTestShellInjectionPrevention.test_validate_dispatch_args_accepts_security_level  s)    11#$=>#	 	2 	
rF   c                 `   |dz  }|j                  dd       t        j                  t        |            }|j                  }d} ||      }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      t        j                  |      t        j                  |      d
z  }	dd|	iz  }
t        t        j                  |
            dx}x}x}x}}|j                  }d} ||      }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      t        j                  |      t        j                  |      d
z  }	dd|	iz  }
t        t        j                  |
            dx}x}x}x}}|j                  }d} ||      }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      t        j                  |      t        j                  |      d
z  }	dd|	iz  }
t        t        j                  |
            dx}x}x}x}}|j                  }d} ||      }d}||k(  }|st        j                  d|fd||f      d	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      t        j                  |      t        j                  |      d
z  }	dd|	iz  }
t        t        j                  |
            dx}x}x}x}}y)u   export KEY=VALUE 형식 파싱	.env.keysun   export API_KEY=abc123
export DB_PASS="secret"
export TOKEN='mytoken'
# 이것은 주석

PLAIN_VAR=plainvalue
utf-8encodingAPI_KEYabc123r   r   rV   r   r   r   NDB_PASSsecretTOKENmytoken	PLAIN_VAR
plainvalue)
write_textr
   load_env_keysrY   r   r/   r0   r1   r2   r3   r4   r5   r6   )r7   r   env_filerV   r9   r:   r<   r   r   r>   r   s              r?   !test_load_env_keys_parses_exportsz>TestShellInjectionPrevention.test_load_env_keys_parses_exports  s   k)%  	 	
 #00X?zz0)0z)$00$0000$000000v000v000z000)000$0000000000zz0)0z)$00$0000$000000v000v000z000)000$0000000000zz/'/z'"/i/"i////"i//////v///v///z///'///"///i///////zz6+6z+&6,6&,6666&,666666v666v666z666+666&666,66666666rF   c                    |dz  }|j                  dd       t        j                  t        |            }d}||v }|st	        j
                  d|fd||f      t	        j                  |      dt        j                         v st	        j                  |      rt	        j                  |      ndd	z  }d
d|iz  }t        t	        j                  |            dx}}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}	}y)u   주석(#)과 빈 줄은 무시rw  u&   # 주석

export VALID_KEY=validvalue
rx  ry  	VALID_KEYrW   r   rV   r   r   r   Nr   r   r   r   r   r]   r^   )r  r
   r  rY   r/   r0   r4   r1   r2   r3   r5   r6   r   )r7   r   r  rV   ri   rf   rg   rh   r<   r   rj   rk   s               r?   .test_load_env_keys_ignores_comments_and_blankszKTestShellInjectionPrevention.test_load_env_keys_ignores_comments_and_blanks  s    k)= 	 	
 #00X?${f$$$${f$$${$$$$$$f$$$f$$$$$$$6{a{a{ass66{arF   c                    t         j                  t        |dz              }i }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y)	u+   파일이 없으면 빈 딕셔너리 반환znonexistent.envr   z%(py0)s == %(py3)srV   r   r   r   N)r
   r  rY   r/   r0   r1   r2   r3   r4   r5   r6   )r7   r   rV   rf   r9   rg   rh   s          r?   test_load_env_keys_missing_filez<TestShellInjectionPrevention.test_load_env_keys_missing_file  su    "00X@Q5Q1RSv|vvvrF   N)rA   rB   rC   rD   r[  rc  rg  rj  rl  ro  rr  ru  r  r  r  rE   rF   r?   rN  rN    s?    FAbF


7$	 rF   rN  c                   D    e Zd ZdZd Zd Zd Zd Z ed      d        Z	y)	TestSaveCompletionMessageu6   _save_completion_message 함수 테스트 (task-923.1)c                 Z   t        j                  t        dt        |            5  |dz  dz  }|j	                  dd       t        j                  dd       ddd       |dz  dz  d	z  j                  d
      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y# 1 sw Y   xY w)u,   메시지를 completion.txt 파일로 저장r   r   r   Tr   rT  u   테스트 완료 메시지Nztask-100.1.completion.txtrx  ry  r   r  savedr   r   r   )r   r   r
   rY   r   _save_completion_message	read_textr/   r0   r1   r2   r3   r4   r5   r6   r7   r   r
  r  rf   r9   rg   rh   s           r?   test_saves_message_to_filez4TestSaveCompletionMessage.test_saves_message_to_file  s    \\+-=s8}M 	c!H,x7JTD966|Eab	c
 H$x/2MMXXbiXj44u44444u4444444u444u44444444444	c 	cs   2D!!D*c           	         t        j                  t        dt        |            5  t        j	                  dd       ddd       d}||z  }d}||z  }d}||z  }|j
                  } |       }	|	sdd	t        j                         v st        j                  |      rt        j                  |      nd	t        j                  |      t        j                  |      t        j                  |      t        j                  |      t        j                  |	      d
z  }
t        t        j                  |
            dx}x}x}x}x}x}x}}	y# 1 sw Y   xY w)u.   events 디렉토리가 없으면 자동 생성r   z
task-200.1rp   Nr   r   ztask-200.1.completion.txtziassert %(py13)s
{%(py13)s = %(py11)s
{%(py11)s = (((%(py0)s / %(py2)s) / %(py5)s) / %(py8)s).exists
}()
}r   )r   r   r   r^   r   py13)r   r   r
   rY   r  r	  r1   r2   r/   r3   r4   r5   r6   )r7   r   r9   r:   r   r;   r   @py_assert9@py_assert10@py_assert12@py_format14s              r?   test_creates_parent_directoryz7TestSaveCompletionMessage.test_creates_parent_directory  s    \\+-=s8}M 	R66|[Q	R $V8#VhV#h.V1LV.1LLVLTTVTVVVVVVVVVVVVVV8VVVhVVV1LVVVTVVVVVVVVVVV	R 	Rs   EEc                 z   |dz  dz  }|j                  dd       |dz  j                  dd       t        j                  t        d	t        |            5  t        j                  d
d       ddd       |dz  j                  d      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            dx}}y# 1 sw Y   xY w)u'   기존 파일이 있으면 덮어쓰기r   r   Tr   ztask-300.1.completion.txtu   이전 메시지rx  ry  r   z
task-300.1u   새 메시지Nr   r  r  r   r   r   )r   r  r   r   r
   rY   r  r  r/   r0   r1   r2   r3   r4   r5   r6   r  s           r?   test_overwrites_existing_filez7TestSaveCompletionMessage.test_overwrites_existing_file  s    (83
5	1	1==>P[b=c\\+-=s8}M 	V66|_U	V 99DDgDV''u''''u''''''u'''u''''''''''		V 	Vs   D11D:c                    t        j                  t        dd      5  t        j                  t        dt	        d            5  t        j                  dd       ddd       ddd       y# 1 sw Y   xY w# 1 sw Y   yxY w)	u'   OSError 발생 시 예외 없이 처리r   z/nonexistent/readonly/pathr   zPermission deniedr~   z
task-400.1rp   N)r   r   r
   r   OSErrorr  )r7   r   s     r?   test_handles_oserror_gracefullyz9TestSaveCompletionMessage.test_handles_oserror_gracefully  ss    \\+-=?[\ 	VdGAT9UV V!::<UV	V 	VV V	V 	Vs#   &A6A*A6*A3	/A66A?r   c           	      2   d }||_         |dz  dz  }|j                  dd       t               }d|_        t	        ddd	g      5  t	        j
                  d
ddd      5  t	        j                  t        dt        |            5  t	        d|      5  t        j                          ddd       ddd       ddd       ddd       |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}}|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  }dd|iz  }t#        t        j$                  |            dx}}d}||
v }|st        j(                  d|fd||
f      t        j                   |      dt        j                         v st        j                  |
      rt        j                   |
      nddz  }dd|iz  }t#        t        j$                  |            dx}}y# 1 sw Y   @xY w# 1 sw Y   ExY w# 1 sw Y   JxY w# 1 sw Y   OxY w) uD   main() 일반 경로에서 .completion.txt가 생성되는지 확인c                 L   t               }d|_        d|_        t        | t              r_dj                  d | D              }d|v r%d|v r!t        j                  ddd d d      |_        |S t        j                  d	d
i      |_        |S t        j                  d	d
i      |_        |S )Nr   r   r   c              3   2   K   | ]  }t        |        y wr   r   r   s     r?   r   zrTestSaveCompletionMessage.test_completion_file_created_in_main_normal_path.<locals>.side_effect.<locals>.<genexpr>  r   r   r   r   Fr   r   r   r   r  s        r?   r   z_TestSaveCompletionMessage.test_completion_file_created_in_main_normal_path.<locals>.side_effect  r  rF   r   r   Tr   rJ   r   r	   z
task-923.1rK   rM   r   r   r   rN   rO   Nztask-923.1.completion.txtu-   .completion.txt 파일이 생성되지 않음r  completion_filer  rx  ry  rW   r   contentr   r   r   r   )r   r   r   r_   r   r`   r   r
   rY   r'   r	  r/   r   r1   r2   r3   r4   r5   r6   r  r0   )r7   r   r   r   r
  rd   r  r9   r:   r  r  ri   rf   rg   rh   s                  r?   0test_completion_file_created_in_main_normal_pathzJTestSaveCompletionMessage.test_completion_file_created_in_main_normal_path  s*   	   +(83
5K	 #	 *5|DE	%JJ||Yg%hi	% LL*,<c(mL	% /	:		% ""$	% 	% 	% 	% %'BB%%Y%'Y'YY+XYYYYYYYYYYYY%YYY'YYYYYY!++W+=&|w&&&&|w&&&|&&&&&&w&&&w&&&&&&&"x7""""x7"""x""""""7"""7"""""""	% 	% 	% 	% 	% 	% 	% 	%sT   L%K?K2K%	'K2/K?7L%K/*K22K<7K??L		LLN)
rA   rB   rC   rD   r  r  r  r  r   r  rE   rF   r?   r  r    s5    @5W
(V &# &#rF   r  )8rD   builtinsr1   _pytest.assertion.rewrite	assertionrewriter/   r   rq   syspathlibr   unittest.mockr   r   rr   r   r+   r/  r#   __file__parent_SCRIPTS_DIRpathinsertrY   importlib.util	importlib_MODULE_PATHutilspec_from_file_locationr   rf   r9   r0   r2   r3   r4   rg   rh   r5   r6   module_from_specr
   loaderr   r:   r=   exec_moduler   rH   r   r   r  rN  r  rE   rF   r?   <module>r     s     	 
  *

1<@ZZ^^,.CD
  H~$$++ 3|$ % 44~~--.A<P t4   t4     t   t   4      NN33D9 {{ $ {$   {$     t   t   {  $         ) *( ("P Pf_^ _^Dw% w%tor ordi iXN# N#rF   