
    iM4                    L   d Z ddlmZ ddlZddlZddlZddlmZ ddlm	Z	 ddl
Z
 ee      j                         j                  j                  j                  Zej                  j!                  d ee             ddlZddlmZ ddlmZ ddlmZ dd	lmZ dd
lmZ ddlm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$ G d 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\  dev7 — Phase ε dispatch 모듈 분리 회귀 테스트 (task-2388).

dispatch.py 4336줄을 dispatch/ 패키지 6 모듈(facade) 로 분리한 결과를 검증한다.

12 단위 테스트 + 5 검증 시나리오:
  1-2. task_id: 4-layer fix 동작 + 동시성 race 0
  3-4. metadata: frontmatter 파싱 + legacy fallback
  5-6. prompt: build_prompt 동작 + 슬림 라인 수
  7-8. retry: status 가드 + archived/escalated 박제
  9-10. core: argparse 진입 + cancel 동작
  11. audit: ALLOWED_COMMANDS / facade 노출
  12. integration: 패키지 import + 외부 호환 동작 회귀 0

  S1. facade 6 모듈 import 모두 동작
  S2. dispatch.py 호환 shim 동작 (--help)
  S3. 외부 mock 호환 (subprocess, logger, WORKSPACE)
  S4. dispatch namespace 모든 함수 노출
  S5. 본체 변경 없음 (dispatch/__init__.py == 원본 dispatch.py)
    )annotationsN)Path)patch)audit)core)prompt)retry)task_id)_statec                      e Zd Zd Zd Zd Zy)TestTaskIdFacadec                    t        t        d      sJ t        t        d      sJ t        t        d      sJ t        t        d      sJ t        t        d      sJ y)uC   task_id facade가 task-2380 fix 함수 5개를 모두 노출한다.generate_task_id_resolve_main_workspace_compute_next_id_from_timers_sync_counter_if_neededget_dispatch_timeN)hasattrdispatch_task_idselfs    T/home/jay/workspace/.worktrees/task-2463-dev6/tests/dev7/test_dispatch_modularize.py'test_facade_exports_task_2380_functionsz8TestTaskIdFacade.test_facade_exports_task_2380_functions0   s\    ');<<<')BCCC')GHHH')BCCC')<===    c                    t         j                  t        j                  u sJ t         j                  t        j                  u sJ y)uB   facade의 함수가 dispatch namespace의 함수와 동일 객체.N)r   r   dispatchr   r   s    r   "test_facade_identity_with_dispatchz3TestTaskIdFacade.test_facade_identity_with_dispatch8   s6    00H4M4MMMM778;[;[[[[r   c           	         |dz  }|j                  t        j                  dddiddiddidi             t        j                  |      }|dk(  s
J d|        y)	u/   task-2380: 4자리 정수 외 변종 ID 무시.task-timers.jsontasksstatusdone)z	task-2386ztask-9.1z
task-200.1iS	  zExpected 2387, got N)
write_textjsondumpsr   r   )r   tmp_path
timer_fileresults       r   (test_compute_next_id_filters_variant_idsz9TestTaskIdFacade.test_compute_next_id_filters_variant_ids=   sv     22
djj&/%v.'0*
  	 ">>zJ~=!4VH==~r   N)__name__
__module____qualname__r   r   r)    r   r   r   r   /   s    >\
>r   r   c                      e Zd Zd Zy)TestTaskIdConcurrencyc                b    ddl }|j                  t        j                        }d|v sJ d|v sJ y)u8   generate_task_id의 코드에 fcntl.flock 호출 보존.r   Nzfcntl.flockLOCK_EX)inspect	getsourcer   r   )r   r2   srcs      r    test_generate_task_id_uses_flockz6TestTaskIdConcurrency.test_generate_task_id_uses_flockO   s7     9 9:###Cr   N)r*   r+   r,   r5   r-   r   r   r/   r/   N   s     r   r/   c                      e Zd Zd Zd Zy)TestMetadataIntegrationc                X    d}t        j                  |      }|J d|v sJ d|d   v sJ y)u-   allowed_resources YAML 파싱 (audit 영역).zx## allowed_resources

```yaml
allowed_resources:
  paths:
    - "src/**"
  commands:
    - "pytest"
  ttl_hours: 24
```
Npathszsrc/**r   _parse_allowed_resources)r   	task_descr(   s      r   !test_parse_allowed_resources_yamlz9TestMetadataIntegration.test_parse_allowed_resources_yaml[   sG    
	 229=!!!&   6'?***r   c                6    t        j                  d      }|J y)u%   yaml 블록이 없으면 None 반환.zplain text without yamlNr:   r   r(   s     r   2test_parse_allowed_resources_no_block_returns_nonezJTestMetadataIntegration.test_parse_allowed_resources_no_block_returns_nonem   s    223LM~~r   N)r*   r+   r,   r=   r@   r-   r   r   r7   r7   Z   s    +$r   r7   c                      e Zd Zd Zd Zy)TestPromptFacadec                l    t        t        d      sJ t        j                  t        j                  u sJ y)u.   prompt facade가 build_prompt를 노출한다.build_promptN)r   dispatch_promptrD   r   r   s    r    test_facade_exports_build_promptz1TestPromptFacade.test_facade_exports_build_promptw   s,    777++x/D/DDDDr   c                    t        j                  t              5  t        j                  ddd       ddd       y# 1 sw Y   yxY w)uD   unknown team_id에 대해 sys.exit 호출 (task-2386 정상 동작).zinvalid-teamtestztask-1)team_idr<   r
   N)pytestraises
SystemExitr   rD   r   s    r   $test_build_prompt_unknown_team_exitsz5TestPromptFacade.test_build_prompt_unknown_team_exits|   s:    ]]:& 	!!&  	 	 	s	   <AN)r*   r+   r,   rF   rM   r-   r   r   rB   rB   v   s    E
r   rB   c                      e Zd Zd Zd Zd Zy)TestRetryFacadec                l    t        t        d      sJ t        j                  t        j                  u sJ y)u1   retry facade가 _set_task_status를 노출한다._set_task_statusN)r   dispatch_retryrQ   r   r   s    r   #test_facade_exports_set_task_statusz3TestRetryFacade.test_facade_exports_set_task_status   s-    ~'9:::..(2K2KKKKr   c                H   |dz  }|j                   j                  dd       |dz  j                  dd       |dz  dz  }|j                  t        j                  ddddiii             |j                  t        d	|       t        j                  dd
      }|du sJ d       y)u:   task-2387: archived 상태는 영구 박제. False 반환.r   Tparentsexist_okmemoryr    ztask-archivedr!   archived	WORKSPACErunningFu(   archived는 변경 차단 (False) 반환N)parentmkdirr#   r$   r%   setattrr   rQ   )r   r&   monkeypatchr'   actual_timerr(   s         r   $test_set_task_status_blocks_archivedz4TestRetryFacade.test_set_task_status_blocks_archived   s     22
t<	H	##D4#@(*-??

o*'=>,
 ! 	 	Hk8<**?IFJ JJr   c                    |dz  j                  dd       |dz  dz  }|j                  t        j                  ddddiii             |j	                  t
        d	|       t        j                  dd
      }|du sJ y)u-   task-2387: escalated 상태도 영구 박제.rX   TrU   r   r    ztask-escr!   	escalatedrZ   r[   FN)r]   r#   r$   r%   r^   r   rQ   )r   r&   r_   r`   r(   s        r   %test_set_task_status_blocks_escalatedz5TestRetryFacade.test_set_task_status_blocks_escalated   s    	H	##D4#@(*-??

j8["9:,
 ! 	 	Hk8<**:yAr   N)r*   r+   r,   rS   ra   rd   r-   r   r   rO   rO      s    L
K
r   rO   c                      e Zd Zd Zd Zd Zy)TestCoreFacadec                    t        t        d      sJ t        t        d      sJ t        t        d      sJ t        j                  t        j                  u sJ y)u4   core facade가 main / dispatch / cancel_task 노출.mainr   cancel_taskN)r   dispatch_corerh   r   r   s    r   (test_facade_exports_main_dispatch_cancelz7TestCoreFacade.test_facade_exports_main_dispatch_cancel   sJ    }f---}j111}m444!!X]]222r   c                    t        j                  t        j                  t	        t
        dz        dgddd      }|j                  dk(  sJ d|j                  v sJ y)	uC   dispatch.py shim이 --help 정상 출력 (script 진입점 동작).dispatch.pyz--helpT   )capture_outputtexttimeoutr   u   작업 위임 디스패처N)
subprocessrunsys
executablestr
_WORKSPACE
returncodestdoutr?   s     r   test_dispatch_py_shim_helpz)TestCoreFacade.test_dispatch_py_shim_help   sY    ^^Sm!;<hG	
   A%%%+v}}<<<r   c                B    t        j                  d      }|d   dk(  sJ y)u:   cancel_task가 잘못된 ID에 대해 error status 반환.z	not-validr!   errorN)r   ri   r?   s     r   )test_cancel_task_invalid_id_returns_errorz8TestCoreFacade.test_cancel_task_invalid_id_returns_error   s%    %%k2h7***r   N)r*   r+   r,   rk   rz   r}   r-   r   r   rf   rf      s    3	=+r   rf   c                      e Zd Zd Zd Zy)TestAuditFacadec                    t        t        d      sJ t        t        d      sJ t        t        d      sJ t        t        d      sJ y)u8   audit facade가 ALLOWED_COMMANDS + 핵심 함수 노출.ALLOWED_COMMANDS_save_capability_snapshot_check_affected_files_overlap_validate_composite_teamsN)r   dispatch_auditr   s    r   +test_facade_exports_constants_and_functionsz;TestAuditFacade.test_facade_exports_constants_and_functions   sF    ~'9:::~'BCCC~'FGGG~'BCCCr   c                H    t         j                  t        j                  u sJ y)u8   ALLOWED_COMMANDS가 dispatch namespace와 동일 객체.N)r   r   r   r   s    r   test_audit_constants_identityz-TestAuditFacade.test_audit_constants_identity   s    ..(2K2KKKKr   N)r*   r+   r,   r   r   r-   r   r   r   r      s    DLr   r   c                      e Zd Zd Zd Zy)TestIntegrationRegressionc                H    dD ]  }t        t        |      rJ d| d        y)uI   dispatch namespace가 분리된 영역의 모든 핵심 함수를 노출.)r   r   r   rQ   _patch_timer_metadatarD   _resolve_resume_inject_project_map_contextr   r   _is_insuro_server_change_inject_platform_rules_auto_inject_affected_filesr   r   ri   rh   z	dispatch.u    누락N)r   r   )r   names     r   #test_dispatch_namespace_exports_allz=TestIntegrationRegression.test_dispatch_namespace_exports_all   s5    
 	FD 8T*EivW,EE*	Fr   c                    ddl m}  |       }d|_        t        d|      5  t	        j
                  dd      }ddd       d	v sJ y# 1 sw Y   xY w)
uT   외부 테스트 호환: patch('dispatch.subprocess.run')이 audit 함수에 영향.r   )	MagicMockzsrc/foo.py
src/bar.py
zdispatch.subprocess.run)return_valueu#   테스트 `Sample` 클래스 수정z
/workspaceNz!## affected_files (auto-detected))unittest.mockr   ry   r   r   r   )r   r&   r   mock_resultr(   s        r   )test_external_test_compat_subprocess_mockzCTestIntegrationRegression.test_external_test_compat_subprocess_mock   sY    +k7,;G 	995|F	 3f<<<		 	s   AAN)r*   r+   r,   r   r   r-   r   r   r   r      s    F$
=r   r   c                      e Zd Zd Zy)TestScenarioS1FacadeImportsc                $    ddl m}m}m}m}m}m} y)u%   6 모듈 facade 모두 import 가능.r   )r   r   r   r   r	   r
   N)r   r   r   r   r   r	   r
   )r   r   r   r   r   r	   r
   s          r   test_all_six_facades_importablez;TestScenarioS1FacadeImports.test_all_six_facades_importable   s    HHHr   N)r*   r+   r,   r   r-   r   r   r   r      s    Ir   r   c                      e Zd Zd Zy)TestScenarioS2ShimCompatc                J    t         dz  j                         }d|v sJ d|v sJ y)u5   dispatch.py shim이 dispatch.core.main으로 routing.rm   zfrom dispatch.core import mainzif __name__ == "__main__"N)rw   	read_text)r   	shim_texts     r   test_shim_routes_to_mainz1TestScenarioS2ShimCompat.test_shim_routes_to_main  s1    -/::<	/9<<<*i777r   N)r*   r+   r,   r   r-   r   r   r   r     s    8r   r   c                      e Zd Zd Zy)TestScenarioS3MockCompatc                4    ddl }dt        j                  v sJ y)uH   patch('dispatch.logger') 적용 시 dispatch namespace 함수에 반영.r   Nlogger)r2   r   __dict__)r   r2   s     r   $test_mock_dispatch_logger_propagatesz=TestScenarioS3MockCompat.test_mock_dispatch_logger_propagates  s    8,,,,,r   N)r*   r+   r,   r   r-   r   r   r   r     s    -r   r   c                      e Zd Zd Zy)TestScenarioS4NamespaceParityc           
         t        t              D cg c]/  }|j                  d      st        t	        t        |d            r|1 }}t        |      dk\  sJ yc c}w )u=   분리 후 dispatch namespace에 핵심 함수 30+개 노출._N   )dirr   
startswithcallablegetattrlen)r   npublic_funcss      r   (test_dispatch_has_minimum_function_countzFTestScenarioS4NamespaceParity.test_dispatch_has_minimum_function_count  s[     8}
<<$'(At2L)M 
 

 < A%%%
s   4AN)r*   r+   r,   r   r-   r   r   r   r     s    &r   r   c                      e Zd Zd Zy)TestScenarioS5BodyUnchangedc                    t         dz  dz  j                         j                  d      }d|cxk  rdk  sn J d| d       y)	uO   dispatch/__init__.py가 원본 dispatch.py 4336줄과 동일 라인 수 (±10).r   z__init__.py
i  i  u   init.py 라인 수 u    (예상 ~4336)N)rw   r   count)r   
init_liness     r    test_init_py_size_matches_legacyz<TestScenarioS5BodyUnchanged.test_init_py_size_matches_legacy*  sH     :-=HHJPPQUV
z)T)\-@O+\\)r   N)r*   r+   r,   r   r-   r   r   r   r   )  s    ]r   r   ),__doc__
__future__r   r$   rr   rt   pathlibr   r   r   rJ   __file__resolver\   rw   pathinsertrv   r   r   r   r   rj   r   rE   r	   rR   r
   r   r   dispatch_stater   r/   r7   rB   rO   rf   r   r   r   r   r   r   r   r-   r   r   <module>r      s   ( #   
    (^##%,,33::
 3z? #  , * . , 0 -> >>    8 & H+ +8
L 
L = =FI I8 8- -& &] ]r   