
    mXi!                       d Z ddlm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Ze	j                  j!                  d e ee      j&                  j&                  j&                               ddlmZmZmZmZmZmZ ddlmZ ej:                  d        Z G 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]   utils/delegate_controller.py + delegate_runner.py 테스트 스위트 (TDD — RED → GREEN)    )annotationsN)Path)BLOCKED_TOOLSMAX_CONCURRENT	MAX_DEPTHDelegateControllerSubAgentResultSubAgentTask)run_subagentc                     t        d      S )u$   기본 깊이(0) DelegateController.r   parent_depth)r        ;/home/jay/workspace/utils/tests/test_delegate_controller.pyctrlr      s     1--r   c                  .    e Zd ZdZd Zd Zd Zd Zd Zy)TestCanDelegateDepthu   깊이 제한 검증c                   |j                         \  }}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}}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)u   깊이 0은 위임 가능.Tisz%(py0)s is %(py3)sokpy0py3assert %(py5)spy5NOK==z%(py0)s == %(py3)sreason)	can_delegate
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanation)selfr   r   r#   @py_assert2@py_assert1@py_format4@py_format6s           r   test_depth_zero_allowedz,TestCanDelegateDepth.test_depth_zero_allowed,   s    &&(
FrTzrTrrTv~vvvr   c                   t        d      }|j                         \  }}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
)u(   깊이 1도 위임 가능 (MAX_DEPTH=2).   r   Tr   r   r   r   r   r   N)
r   r$   r%   r&   r'   r(   r)   r*   r+   r,   r-   cr   _r.   r/   r0   r1   s           r   test_depth_one_allowedz+TestCanDelegateDepth.test_depth_one_allowed2   st    A. ArTzrTrrTr   c                   t        t              }|j                         \  }}d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d	x}}t        t              }||v }|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                  t              rt        j                  t              nd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	)u(   깊이가 MAX_DEPTH 이상이면 거부.r   Fr   r   r   r   r   r   Ninz0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} in %(py5)sstrr   r#   r   py1r   r   assert %(py7)spy7)r   r   r$   r%   r&   r'   r(   r)   r*   r+   r,   r=   )
r-   r6   r   r#   r.   r/   r0   r1   @py_assert4@py_format8s
             r   test_depth_at_max_deniedz-TestCanDelegateDepth.test_depth_at_max_denied8   s   I6^^%
FrU{rUrrU9~'~''''~''''''s'''s''''''9'''9'''~''''''''''''''''r   c                   t        t        dz         }|j                         \  }}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
)u   MAX_DEPTH+1 깊이는 거부.r4   r   Fr   r   r   r   r   r   N)r   r   r$   r%   r&   r'   r(   r)   r*   r+   r,   r5   s           r   test_depth_exceeds_max_deniedz2TestCanDelegateDepth.test_depth_exceeds_max_denied?   sx    IM: ArU{rUrrUr   c                   t         dz   }t        |      }|j                         \  }}t        |      }||v }|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                  |      dt        j                         v st	        j                  |      rt	        j                  |      nddz  }d	d
|iz  }t        t	        j                  |            dx}}y)u/   거부 이유에 현재 깊이가 포함된다.r4   r   r:   r<   r=   depthr#   r>   r@   rA   N)r   r   r$   r=   r%   r&   r'   r(   r)   r*   r+   r,   )	r-   rH   r6   r7   r#   r.   rB   r1   rC   s	            r   "test_reason_contains_current_depthz7TestCanDelegateDepth.test_reason_contains_current_depthE   s    AE2NN$	65z#zV####zV######s###s######5###5###z######V###V#######r   N)	__name__
__module____qualname____doc__r2   r8   rD   rF   rI   r   r   r   r   r   )   s    ($r   r   c                      e Zd ZdZd Zd Zy)TestCanDelegateConcurrencyu   동시 자식 수 제한 검증c                   |j                   5  |j                  j                  dgt        z         ddd       |j	                         \  }}d}||u }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd	|iz  }t        t        j                  |            dx}}t        t              }||v }|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                  t              rt        j                  t              nd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)u0   _active_children이 MAX_CONCURRENT이면 거부.dummyNFr   r   r   r   r   r   r:   r<   r=   r   r#   r>   r@   rA   )_children_lock_active_childrenextendr   r$   r%   r&   r'   r(   r)   r*   r+   r,   r=   )
r-   r   r   r#   r.   r/   r0   r1   rB   rC   s
             r   test_concurrent_limit_respectedz:TestCanDelegateConcurrency.test_concurrent_limit_respectedU   sH      	E!!(('^)CD	E&&(
FrU{rUrrU>","f,,,,"f,,,,,,s,,,s,,,,,,>,,,>,,,",,,,,,f,,,f,,,,,,,		E 	Es   $H77Ic                   |j                   5  |j                  j                  dgt        dz
  z         ddd       |j	                         \  }}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)u7   _active_children이 MAX_CONCURRENT 미만이면 허용.rQ   r4   NTr   r   r   r   r   r   )rR   rS   rT   r   r$   r%   r&   r'   r(   r)   r*   r+   r,   )r-   r   r   r7   r.   r/   r0   r1   s           r   #test_concurrent_below_limit_allowedz>TestCanDelegateConcurrency.test_concurrent_below_limit_allowed]   s       	K!!(('nq6H)IJ	K!!#ArTzrTrrT	K 	Ks   'C55C>N)rJ   rK   rL   rM   rU   rW   r   r   r   rO   rO   R   s    )-r   rO   c                  .    e Zd ZdZd Zd Zd Zd Zd Zy)TestFilterToolsu   BLOCKED_TOOLS 필터링c                    t        t              ddgz   }|j                  |      }t        D ]  }||v}|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  |      rt        j                  |      nddz  }dd	|iz  }t        t        j                  |            d
} y
)u%   BLOCKED_TOOLS 항목이 제거된다.	read_filebash)not in)z%(py0)s not in %(py2)sblockedresultr   py2assert %(py4)spy4N)listr   filter_toolsr%   r&   r'   r(   r)   r*   r+   r,   )r-   r   toolsr_   r^   r/   @py_format3@py_format5s           r   test_blocked_tools_removedz*TestFilterTools.test_blocked_tools_removedm   s    ]#{F&;;""5)$ 	)G&((((7&((((((7(((7((((((&(((&(((((((	)r   c                   g d}|j                  |      }||k(  }|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      nddt        j                         v st        j
                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                  |            d	}y	)
u   허용 도구는 보존된다.)r[   r\   
write_filer    )z%(py0)s == %(py2)sr_   rf   r`   rb   rc   N	re   r%   r&   r'   r(   r)   r*   r+   r,   )r-   r   rf   r_   r/   rg   rh   s          r   test_allowed_tools_keptz'TestFilterTools.test_allowed_tools_keptt   s~    3""5)vvvr   c                    |j                   }d} ||      }g }||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)   toolsets=None이면 빈 리스트 반환.Nr    zR%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.filter_tools
}(%(py4)s)
} == %(py9)sr   r   ra   rc   py6py9assert %(py11)spy11rl   	r-   r   r/   @py_assert3@py_assert5@py_assert8@py_assert7@py_format10@py_format12s	            r    test_none_toolsets_returns_emptyz0TestFilterTools.test_none_toolsets_returns_emptyz   s      ,, &,",&",,,,&",,,,,,t,,,t,,, ,,,,,,&,,,",,,,,,,,r   c                    |j                   }g } ||      }g }||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.   빈 리스트 입력 시 빈 리스트 반환.r    ro   r   rp   rs   rt   Nrl   ru   s	            r   !test_empty_toolsets_returns_emptyz1TestFilterTools.test_empty_toolsets_returns_empty~   s      ** $**$****$******t***t*** ******$***********r   c                   |j                  t        t                    }g }||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&   모두 차단 도구면 빈 리스트.r    r"   r_   r   r   r   N)re   rd   r   r%   r&   r'   r(   r)   r*   r+   r,   )r-   r   r_   r.   r/   r0   r1   s          r   test_only_blocked_returns_emptyz/TestFilterTools.test_only_blocked_returns_empty   so    ""4#67v|vvvr   N)	rJ   rK   rL   rM   ri   rm   r|   r~   r   r   r   r   rY   rY   j   s    !)-+r   rY   c                  :    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
y	)
TestRunBatchu    배치 서브에이전트 실행c                r   |j                  g       }g }||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*   빈 태스크 목록은 빈 결과 반환.r    r"   resultsr   r   r   N)	runr%   r&   r'   r(   r)   r*   r+   r,   )r-   r   r   r.   r/   r0   r1   s          r   test_empty_tasks_returns_emptyz+TestRunBatch.test_empty_tasks_returns_empty   sg    ((2,w"}w"ww"r   c                `   t        d      g}|j                  |      }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'   단일 태스크는 결과 1개 반환.z	test goalgoalr4   r    z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenr   r   r?   r   rq   assert %(py8)spy8N)r
   r   r   r%   r&   r'   r(   r)   r*   r+   r,   )	r-   r   tasksr   r.   rw   rB   @py_format7@py_format9s	            r   #test_single_task_returns_one_resultz0TestRunBatch.test_single_task_returns_one_result   s    ;/0((5/7| q |q    |q      s   s      7   7   |   q       r   c                   t        d      D cg c]  }t        d|        }}|j                  |      }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c c}w )u,   결과 수가 태스크 수와 일치한다.   task r   r    r   r   r   r   r   r   N)ranger
   r   r   r%   r&   r'   r(   r)   r*   r+   r,   )
r-   r   ir   r   r.   rw   rB   r   r   s
             r    test_results_count_matches_tasksz-TestRunBatch.test_results_count_matches_tasks   s    9>qBAU1#;/BB((5/7| q |q    |q      s   s      7   7   |   q        Cs   Ec                   t        d      D cg c]  }t        d|        }}|j                  |      }t        d |D              }g 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c c}w )u6   각 결과의 task_index가 올바르게 할당된다.r   r   r   c              3  4   K   | ]  }|j                     y w)N)
task_index).0rs     r   	<genexpr>zBTestRunBatch.test_task_index_assigned_correctly.<locals>.<genexpr>   s     7!7s   )r   r4      r    r"   indicesr   r   r   N)r   r
   r   sortedr%   r&   r'   r(   r)   r*   r+   r,   )
r-   r   r   r   r   r   r.   r/   r0   r1   s
             r   "test_task_index_assigned_correctlyz/TestRunBatch.test_task_index_assigned_correctly   s    9>qBAU1#;/BB((5/7w77##w)####w)######w###w###)####### Cs   C2c                6   h d}t        d      g}|j                  |      }|D ]  }|j                  }||v }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      nd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)u(   결과의 status가 유효한 값이다.>   errorfailed	completedinterruptedhellor   r:   )z.%(py2)s
{%(py2)s = %(py0)s.status
} in %(py4)sr   valid_statuses)r   ra   rc   zassert %(py6)srq   N)r
   r   statusr%   r&   r'   r(   r)   r*   r+   r,   )
r-   r   r   r   r   r   r/   rv   rh   r   s
             r   test_result_status_is_validz(TestRunBatch.test_result_status_is_valid   s    H7+,((5/ 	.A88-8~----8~------1---1---8------~---~-------	.r   c                2   t        d      g}|j                  |      }|d   }t        |t              }|sddt	        j
                         v st        j                  t              rt        j                  t              ndt        j                  |      dt	        j
                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }t        t        j                  |            dx}}y)	u&   반환 타입이 SubAgentResult이다.testr   r   z5assert %(py5)s
{%(py5)s = %(py0)s(%(py2)s, %(py3)s)
}
isinstancer	   )r   ra   r   r   N)r
   r   r   r	   r'   r(   r%   r)   r*   r+   r,   )r-   r   r   r   r/   rB   r1   s          r   #test_result_is_subagent_result_typez0TestRunBatch.test_result_is_subagent_result_type   s    6*+((5/!!*5z*n55555555z555z555*555555n555n5555555555r   c                   t        d      g}|j                  |      }|d   }|j                  }d}||k\  }|st        j                  d|fd||f      t        j
                  |      t        j
                  |      t        j
                  |      dz  }dd	|iz  }	t        t        j                  |	            d
x}x}x}}y
)u#   duration_seconds가 0 이상이다.r   r   r           >=)z8%(py3)s
{%(py3)s = %(py1)s.duration_seconds
} >= %(py6)sr?   r   rq   r   r   N)r
   r   duration_secondsr%   r&   r*   r+   r,   
r-   r   r   r   @py_assert0r.   rw   rB   r   r   s
             r   test_duration_is_non_negativez*TestRunBatch.test_duration_is_non_negative   s    6*+((5/qz1z**1c1*c1111*c111z111*111c1111111r   N)rJ   rK   rL   rM   r   r   r   r   r   r   r   r   r   r   r   r      s(    *
!!$.62r   r   c                      e Zd ZdZd Zd Zy)TestInterruptChildrenu   인터럽트 전파c                   |j                          |j                  }|j                  } |       }|sddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      t        j                  |      dz  }t        t        j                  |            dx}x}}y)u;   interrupt_children() 호출 후 _interrupted가 set 된다.zcassert %(py6)s
{%(py6)s = %(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s._interrupted
}.is_set
}()
}r   )r   ra   rc   rq   N)
interrupt_children_interruptedis_setr'   r(   r%   r)   r*   r+   r,   )r-   r   r/   rv   rw   r   s         r   test_interrupt_sets_eventz/TestInterruptChildren.test_interrupt_sets_event   s    !  ) '')'))))))))t)))t))) )))'))))))))))r   c                   |j                          t        d      g}|j                  |      }|d   }|j                  }h d}||v }|st	        j
                  d|fd||f      t	        j                  |      t	        j                  |      t	        j                  |      dz  }dd	|iz  }	t        t	        j                  |	            d
x}x}x}}y
)uG   인터럽트 후 run()이 'interrupted' 상태를 포함할 수 있다.r   r   r   >   r   r   r   r   r:   )z.%(py3)s
{%(py3)s = %(py1)s.status
} in %(py6)sr   r   r   N)	r   r
   r   r   r%   r&   r*   r+   r,   r   s
             r   test_interrupted_run_statusz1TestInterruptChildren.test_interrupted_run_status   s    !6*+((5/qzSz  S$SS $SSSSS $SSSSzSSS SSS$SSSSSSSSr   N)rJ   rK   rL   rM   r   r   r   r   r   r   r      s    *
Tr   r   c                  (    e Zd ZdZd Zd Zd Zd Zy)TestRunSubagentu   run_subagent 래퍼 동작c                   t        d      }t        j                         }t        |d|d      }t	        |t
              }|sddt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      nddt        j                         v st        j                  t
              rt        j                  t
              ndt        j                  |      d	z  }t        t        j                  |            d
}y
)u&   SubAgentResult 타입을 반환한다.zsimple taskr   r   rH   r   r   z5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}r   r_   r	   )r   r?   ra   rc   N)r
   	threadingEventr   r   r	   r'   r(   r%   r)   r*   r+   r,   )r-   taskeventr_   rv   rh   s         r   test_returns_subagent_resultz,TestRunSubagent.test_returns_subagent_result   s    /!d!1M&.11111111z111z111111&111&111111.111.1111111111r   c                   t        d      }t        j                         }t        |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}}y)u%   task_index가 결과에 보존된다.r   r   r      r   r    )z2%(py2)s
{%(py2)s = %(py0)s.task_index
} == %(py5)sr_   r   ra   r   r@   rA   N)r
   r   r   r   r   r%   r&   r'   r(   r)   r*   r+   r,   	r-   r   r   r_   r/   rB   rv   r1   rC   s	            r   test_task_index_preservedz)TestRunSubagent.test_task_index_preserved   s    (!d!1M  %A% A%%%% A%%%%%%v%%%v%%% %%%A%%%%%%%r   c                   t        d      }t        j                         }|j                          t	        |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}}y)u=   인터럽트 이벤트가 set된 경우 status='interrupted'.r   r   r   r   r   r    )z.%(py2)s
{%(py2)s = %(py0)s.status
} == %(py5)sr_   r   r@   rA   N)r
   r   r   setr   r   r%   r&   r'   r(   r)   r*   r+   r,   r   s	            r   *test_interrupted_event_returns_interruptedz:TestRunSubagent.test_interrupted_event_returns_interrupted   s    (!		d!1M}}--}----}------v---v---}----------r   c                   t        d      }t        j                         }t        |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}}y)u(   정상 실행 시 duration_seconds >= 0.r   r   r   r   r   r   )z8%(py2)s
{%(py2)s = %(py0)s.duration_seconds
} >= %(py5)sr_   r   r@   rA   N)r
   r   r   r   r   r%   r&   r'   r(   r)   r*   r+   r,   r   s	            r   $test_duration_positive_on_normal_runz4TestRunSubagent.test_duration_positive_on_normal_run   s    (!d!1M&&-#-&#----&#------v---v---&---#-------r   N)rJ   rK   rL   rM   r   r   r   r   r   r   r   r   r      s    $2&..r   r   )%rM   
__future__r   builtinsr'   _pytest.assertion.rewrite	assertionrewriter%   sysr   timepathlibr   pytestpathinsertr=   __file__parentutils.delegate_controllerr   r   r   r   r	   r
   utils.delegate_runnerr   fixturer   r   rO   rY   r   r   r   r   r   r   <module>r      s    c "   
     3tH~,,33::; <  / . .!$ !$R 0 F-2 -2jT T,. .r   