
    i3V                        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 ddlZ ee      j"                  j"                  Ze	j&                  j)                  d ee             ddlmZmZmZmZmZmZmZ ddlmZm Z 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)y)u/  
test_dispatch_gate.py

보리스 게이트 시스템 Phase 1 단위 테스트 (아르고스 작성, task-1838)

테스트 항목:
1. gate_instructions 동작 (GATE_INSTRUCTIONS 딕셔너리, get_gate_instructions, format_for_prompt)
2. affected_files 파싱 (_parse_affected_files)
3. affected_files 겹침 감지 (_check_affected_files_overlap)
4. Lv.2+ affected_files 미기재 경고 (_warn_missing_affected_files)
5. batch_id 완료 추적 (check_batch_completion)
6. 레벨 자동 추정 (_estimate_task_level)
7. task 레벨 파싱 (_parse_task_level)
    N)Path)	MagicMockpatch)_check_affected_files_overlap_estimate_task_level_get_ast_blast_radius_parse_affected_files_parse_task_level_warn_missing_affected_filescheck_batch_completion)GATE_INSTRUCTIONSformat_for_promptget_gate_instructionsc                   *    e Zd Zd Zd Zd Zd Zd Zy)TestGateInstructionsc           	         t        d      D ]  }|t        v }|st        j                  d|fd|t        f      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dz  }t        j                  d| d      d	z   d
|iz  }t        t        j                  |            d}t        |   }d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  }t        j                  d| d| d      d	z   d
|iz  }t        t        j                  |            d}  y)uT   모든 레벨(0-4) 딕셔너리가 존재하고 g1/g2/g3 키를 포함해야 한다.   in)z%(py0)s in %(py2)slevelr   )py0py2u   레벨 u   이 GATE_INSTRUCTIONS에 없음z
>assert %(py4)spy4N)g1g2g3keygatesu   에 'u   ' 키 없음)ranger   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_saferepr_format_assertmsgAssertionError_format_explanation)selfr   @py_assert1@py_format3@py_format5r   r   s          //home/jay/workspace/tests/test_dispatch_gate.py$test_gate_instructions_levels_0_to_4z9TestGateInstructions.test_gate_instructions_levels_0_to_42   s4   1X 	ME--___5-______5___5______-___-____?^/_______%e,E) Me|LLLseLLLLLLsLLLsLLLLLLeLLLeLLLLwugU3%|%LLLLLLLM	M    c                    t        d      }t        d   }||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }t        j                  d      dz   d	|iz  }t        t        j                  |            d
x}}y
)uX   존재하지 않는 레벨(999) 요청 시 레벨 0 딕셔너리로 폴백해야 한다.i  r   ==z%(py0)s == %(py3)sresultr   py3u4   unknown 레벨은 레벨 0으로 폴백되어야 함
>assert %(py5)spy5N)r   r   r    r!   r"   r#   r$   r%   r&   r'   r(   )r)   r4   @py_assert2r*   @py_format4@py_format6s         r-   -test_gate_instructions_unknown_level_fallbackzBTestGateInstructions.test_gate_instructions_unknown_level_fallback:   sv    &s+*1-ev--eeev-eeeeeeveeeveee-eee/eeeeeeer/   c                 |   t        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}}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
)uH   Lv.0 → G1은 비어있어 스킵, G2/G3만 포함된 문자열 반환.r      [G1 설계 게이트])not in)z%(py1)s not in %(py3)sr4   py1r6   u%   Lv.0에서 G1은 포함되면 안 됨r7   r8   N   [G2 구현 게이트]r   z%(py1)s in %(py3)su$   Lv.0에서 G2가 포함되어야 함   [G3 머지 게이트]u$   Lv.0에서 G3가 포함되어야 함
r   r    r!   r%   r"   r#   r$   r&   r'   r(   r)   r4   @py_assert0r9   r:   r;   s         r-   test_format_for_prompt_level_0z3TestGateInstructions.test_format_for_prompt_level_0?   sD   "1%&]&f4]]]&f]]]&]]]]]]f]]]f]]]]6]]]]]]]&X&&0XXX&&XXX&XXXXXX&XXX&XXXX2XXXXXXX&X&&0XXX&&XXX&XXXXXX&XXX&XXXX2XXXXXXXr/   c                    t        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}}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
)uU   Lv.2 → G1/G2/G3 모두 포함되고 'affected_files' 텍스트가 있어야 한다.   r>   r   rC   r4   r@   u$   Lv.2에서 G1이 포함되어야 함r7   r8   NrB   u$   Lv.2에서 G2가 포함되어야 함rD   u$   Lv.2에서 G3가 포함되어야 함affected_filesu?   Lv.2 G1에는 'affected_files' 텍스트가 포함되어야 함rE   rF   s         r-   test_format_for_prompt_level_2z3TestGateInstructions.test_format_for_prompt_level_2F   s   "1%&X&&0XXX&&XXX&XXXXXX&XXX&XXXX2XXXXXXX&X&&0XXX&&XXX&XXXXXX&XXX&XXXX2XXXXXXX&X&&0XXX&&XXX&XXXXXX&XXX&XXXX2XXXXXXXl6)lll6lllllllll6lll6llll+lllllllr/   c                    t        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
)uC   Lv.4 → 'Graduated Auto-Gate' 텍스트가 포함되어야 한다.   zGraduated Auto-Gater   rC   r4   r@   uD   Lv.4 G3에는 'Graduated Auto-Gate' 텍스트가 포함되어야 함r7   r8   NrE   rF   s         r-   test_format_for_prompt_level_4z3TestGateInstructions.test_format_for_prompt_level_4N   st    "1%$v$.vvv$vvv$vvvvvvvvvvvvv0vvvvvvvr/   N)__name__
__module____qualname__r.   r<   rH   rL   rO    r/   r-   r   r   1   s     Mf
Ymwr/   r   c                   N    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)TestParseAffectedFilesc                    d}t        |      }ddg}||k(  }|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dz  }t        j                  d|       d	z   d
|iz  }t        t        j                  |            dx}}y)uG   'affected_files: server.py, app.js' → ['server.py', 'app.js'] 파싱.z!affected_files: server.py, app.js	server.pyapp.jsr1   r3   r4   r5   u   파싱 결과 불일치: r7   r8   N
r	   r    r!   r"   r#   r$   r%   r&   r'   r(   r)   	task_descr4   r9   r*   r:   r;   s          r-   test_parse_affected_files_basicz6TestParseAffectedFiles.test_parse_affected_files_basicZ   s    7	&y1%x0Vv00VVVv0VVVVVVvVVVvVVV0VVV4MfX2VVVVVVVr/   c                    d}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  }t        j                  d| d      dz   d	|iz  }t        t        j                  |            d
x}}y
)uH   affected_files 라인이 없으면 빈 리스트를 반환해야 한다.uS   이 작업은 affected_files 라인이 없습니다.
단순 수정 작업입니다.r1   r3   r4   r5   u!   빈 리스트를 기대했으나 u    반환r7   r8   NrY   rZ   s          r-   test_parse_affected_files_emptyz6TestParseAffectedFiles.test_parse_affected_files_empty`   s{    j	&y1Pv|PPPvPPPPPPvPPPvPPPPPP@PPPPPPPr/   c                    d}t        |      }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  }t        j                  d|       dz   d	|iz  }t        t        j                  |            d
x}}y
)uI   파일명 사이의 공백이 올바르게 처리(strip)되어야 한다.z4affected_files:  server.py ,   app.js  ,  utils.py  )rW   rX   utils.pyr1   r3   r4   r5   u   공백 처리 실패: r7   r8   NrY   rZ   s          r-   %test_parse_affected_files_with_spacesz<TestParseAffectedFiles.test_parse_affected_files_with_spacesf   s    J	&y1<_v<<___v<______v___v___<___@VW]V^>_______r/   c                    d}t        |      }ddg}||k(  }|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dz  }t        j                  d|       d	z   d
|iz  }t        t        j                  |            dx}}y)uU   여러 줄 task_desc에서 affected_files 라인만 정확히 파싱되어야 한다.u   # task-100: 다중 파일 수정
레벨: Lv.2
affected_files: dispatch.py, config/loader.py

## 작업 내용
dispatch.py를 리팩토링합니다.
dispatch.pyzconfig/loader.pyr1   r3   r4   r5   u   다중 라인 파싱 실패: r7   r8   NrY   rZ   s          r-   #test_parse_affected_files_multilinez:TestParseAffectedFiles.test_parse_affected_files_multilinel   s    6 	 'y1');<fv<<fffv<ffffffvfffvfff<fff@]^d]e>fffffffr/   c                    d}t        |      }ddg}||k(  }|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dz  }t        j                  d|       d	z   d
|iz  }t        t        j                  |            dx}}y)ug   ## affected_files 섹션 형식 파싱 — 하이픈 목록을 파일 리스트로 반환해야 한다.z>## affected_files
- dispatch.py
- tests/test_dispatch_gate.py
rc   ztests/test_dispatch_gate.pyr1   r3   r4   r5   u   섹션 형식 파싱 실패: r7   r8   NrY   rZ   s          r-   (test_parse_affected_files_section_formatz?TestParseAffectedFiles.test_parse_affected_files_section_formaty   s    . 	
 'y1')FGqvGGqqqvGqqqqqqvqqqvqqqGqqqKhiohpIqqqqqqqr/   c                    d}t        |      }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  }t        j                  d|       dz   d	|iz  }t        t        j                  |            d
x}}y
)u`   실제 task 파일과 유사한 전체 구조에서 섹션 형식 파싱이 동작해야 한다.u   # Task 수정 작업
## 배경
기존 파서에 버그 있음

## affected_files
- src/main.py
- src/utils/helper.py
- tests/test_main.py

## 작업 내용
파서를 수정합니다.
)zsrc/main.pyzsrc/utils/helper.pyztests/test_main.pyr1   r3   r4   r5   u$   전체 구조 섹션 파싱 실패: r7   r8   NrY   rZ   s          r-   5test_parse_affected_files_section_format_with_contextzLTestParseAffectedFiles.test_parse_affected_files_section_format_with_context   s    
+ 	 'y1U  	GvUU  	G  	G  	GvU  	G  	G  	G  	G  	G  	Gv  	G  	G  	Gv  	G  	G  	GU  	G  	G  	GY}  E  ~F  XG  	G  	G  	G  	G  	G  	Gr/   c                    d}t        |      }ddg}||k(  }|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dz  }t        j                  d|       d	z   d
|iz  }t        t        j                  |            dx}}y)uX   ## affected_files 섹션은 다음 ## 헤더에서 목록 수집을 중단해야 한다.u   ## affected_files
- alpha.py
- beta.py

## 다른 섹션
이 내용은 파일 목록이 아닙니다
- 이것도 파일이 아님
zalpha.pyzbeta.pyr1   r3   r4   r5   u#   다음 헤더에서 종료 실패: r7   r8   NrY   rZ   s          r-   7test_parse_affected_files_section_stops_at_next_headingzNTestParseAffectedFiles.test_parse_affected_files_section_stops_at_next_heading   s    - 	 'y1$i0`v00```v0``````v```v```0```4WX^W_2```````r/   c                    d}t        |      }ddg}||k(  }|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dz  }t        j                  d|       d	z   d
|iz  }t        t        j                  |            dx}}y)uj   기존 인라인 형식(affected_files: ...)은 섹션 형식 추가 후에도 계속 동작해야 한다.z&affected_files: server.py, config.yamlrW   zconfig.yamlr1   r3   r4   r5   u'   인라인 형식 하위 호환 실패: r7   r8   NrY   rZ   s          r-   ,test_parse_affected_files_inline_still_workszCTestParseAffectedFiles.test_parse_affected_files_inline_still_works   s    <	&y1%}5iv55iiiv5iiiiiiviiiviii5iii9`ag`h7iiiiiiir/   c                    d}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  }t        j                  d|       dz   d|iz  }t        t        j                  |            d	x}}y	)
um   ## affected_files 섹션 헤더만 있고 하이픈 목록이 없으면 빈 리스트를 반환해야 한다.u+   ## affected_files

## 작업 내용
없음
r1   r3   r4   r5   u1   빈 섹션은 빈 리스트를 반환해야 함: r7   r8   NrY   rZ   s          r-   ,test_parse_affected_files_section_empty_listzCTestParseAffectedFiles.test_parse_affected_files_section_empty_list   s     	 'y1Yv|YYYvYYYYYYvYYYvYYYYYYPQWPXYYYYYYYr/   c                    d}t        |      }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  }t        j                  d|       dz   d	|iz  }t        t        j                  |            d
x}}y
)uV   ## affected_files 헤더 아래 쉼표 구분 인라인 값도 파싱되어야 한다.uv   ## affected_files
dashboard/wiki_engine.py, dashboard/routes_post.py, dashboard/routes_get.py

## 검증 시나리오
)dashboard/wiki_engine.pyzdashboard/routes_post.pyzdashboard/routes_get.pyr1   r3   r4   r5   u'   섹션 인라인 쉼표 파싱 실패: r7   r8   NrY   rZ   s          r-   .test_parse_affected_files_section_inline_commazETestParseAffectedFiles.test_parse_affected_files_section_inline_comma   s    ' 	 'y1
 	>v 
 
 	> ->,=		>v 
 	> 	> 8>v		> 	> &>%=	  	> 	> 5>I	  	> 	> 5>I	
 	> 	> ->,=4VH=		> 	> 	> +>*=		> 	>r/   c                    d}t        |      }dg}||k(  }|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dz  }t        j                  d|       dz   d	|iz  }t        t        j                  |            d
x}}y
)uU   ## affected_files 아래 괄호 코멘트(예: '(신규)')가 제거되어야 한다.u?   ## affected_files
dashboard/wiki_engine.py (신규)

## 다음
rp   r1   r3   r4   r5   u    괄호 코멘트 제거 실패: r7   r8   NrY   rZ   s          r-   5test_parse_affected_files_section_inline_with_commentzLTestParseAffectedFiles.test_parse_affected_files_section_inline_with_comment   s     	 'y145bv55bbbv5bbbbbbvbbbvbbb5bbb9YZ`Ya7bbbbbbbr/   N)rP   rQ   rR   r\   r^   ra   rd   rf   rh   rj   rl   rn   rq   rs   rS   r/   r-   rU   rU   Y   sC    WQ`grG$aj	Z>	cr/   rU   c                   8    e Zd ZdededefdZd Zd Zd Zd Z	y	)
TestCheckAffectedFilesOverlaptmp_pathtasksreturnc                     |dz  }|j                  dd       |dz  }|j                  t        j                  d|idd      d	
       |S uS   task-timers.json을 tmp_path/memory/ 하위에 생성하고 경로를 반환한다.memoryT)parentsexist_okztask-timers.jsonrw   FrJ   )ensure_asciiindentzutf-8)encodingmkdir
write_textjsondumpsr)   rv   rw   
memory_dir
timer_files        r-   _make_timer_filez.TestCheckAffectedFilesOverlap._make_timer_file   ]    (
5"44
JJ'eAF 	 	
 r/   c                    ddl }|j                  |d|       t        dgd      }g }||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      d	z  }t        j                  d
|       dz   d|iz  }t        t        j                  |            dx}}y)u8   task-timers.json이 없을 때 → 빈 리스트 반환.r   N	WORKSPACErW   ztask-999r1   r3   r4   r5   u>   타이머 파일 없을 때 빈 리스트를 기대했으나: r7   r8   )dispatchsetattrr   r    r!   r"   r#   r$   r%   r&   r'   r(   )	r)   rv   monkeypatchr   r4   r9   r*   r:   r;   s	            r-    test_check_overlap_no_timer_filez>TestCheckAffectedFilesOverlap.test_check_overlap_no_timer_file   s    Hk8<.}jIfv|fffvffffffvfffvffffff]^d]efffffffr/   c                 b   ddl }dddddgdi}| j                  ||       |j                  |d	|       t        dgd
      }t	        |      }d}||kD  }|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 |D        }t        |      }|st        j                  d|       dz   dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}y)ua   다른 running task가 같은 파일을 수정 중이면 경고 메시지를 반환해야 한다.r   Ntask-100running	dev2-teamrW   r`   statusteam_idrK   r   task-200)>)z/%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} > %(py6)slenr4   )r   rA   r6   py6u   겹침이 감지되어야 함z
>assert %(py8)spy8c              3   $   K   | ]  }d |v  
 yw)rW   NrS   ).0ws     r-   	<genexpr>zLTestCheckAffectedFilesOverlap.test_check_overlap_detected.<locals>.<genexpr>   s     4;!#4s   u0   server.py 겹침 경고가 포함되어야 함: z.
>assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}any)r   r   r   )r   r   r   r   r   r    r!   r"   r#   r$   r%   r&   r'   r(   r   )r)   rv   r   r   rw   r4   r9   @py_assert5@py_assert4@py_format7@py_format9r*   @py_assert3r,   s                 r-   test_check_overlap_detectedz9TestCheckAffectedFilesOverlap.test_check_overlap_detected   sa    #&#.
";
 	h.Hk8<.}jI6{?Q?{Q???{Q??????s???s??????6???6???{???Q??? ????????4V4qs44q4qq8hiohp6qqqqqqqsqqqsqqq4qqq4qqqqqqr/   c                    ddl }dddddgdi}| j                  ||       |j                  |d	|       t        d
dgd      }g }||k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }t	        j                  d|       dz   d|iz  }	t        t	        j                  |	            dx}}y)uK   affected_files가 겹치지 않으면 빈 리스트를 반환해야 한다.r   Nr   r   r   zother_module.pyr`   r   r   rW   rX   r   r1   r3   r4   r5   u3   겹침이 없어야 하는데 경고가 반환됨: r7   r8   r   r   r   r   r    r!   r"   r#   r$   r%   r&   r'   r(   
r)   rv   r   r   rw   r4   r9   r*   r:   r;   s
             r-   test_check_overlap_no_conflictz<TestCheckAffectedFilesOverlap.test_check_overlap_no_conflict   s     #&#4j"A
 	h.Hk8<.X/F
S[v|[[[v[[[[[[v[[[v[[[[[[RSYRZ[[[[[[[r/   c                     ddl }ddddgdi}| j                  ||       |j                  |d|       t        dgd      }g }||k(  }|st	        j
                  d	|fd
||f      dt        j                         v st	        j                  |      rt	        j                  |      ndt	        j                  |      dz  }t	        j                  d|       dz   d|iz  }	t        t	        j                  |	            dx}}y)uT   현재 task_id와 동일한 엔트리는 겹침 감지에서 제외되어야 한다.r   Nr   r   z	dev1-teamrW   r   r   r1   r3   r4   r5   u3   자기 자신은 겹침에서 제외되어야 함: r7   r8   r   r   s
             r-    test_check_overlap_self_excludedz>TestCheckAffectedFilesOverlap.test_check_overlap_self_excluded  s     #&#.-
 	h.Hk8< /}jI[v|[[[v[[[[[[v[[[v[[[[[[RSYRZ[[[[[[[r/   N)
rP   rQ   rR   r   dictr   r   r   r   r   rS   r/   r-   ru   ru      s4    	 	d 	t 	gr$\"\r/   ru   c                       e Zd Zd Zd Zd Zy)TestWarnMissingAffectedFilesc                    d}t        |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  }t        j                  d	      d
z   d|iz  }t        t        j                  |            dx}}t        |t              }|s!t        j                  d      dz   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<   Lv.2 + affected_files 미기재 → 경고 문자열 반환.uL   # task-100: 수정
레벨: Lv.2
작업 내용: 무언가를 수정합니다.rJ   
task_levelN)is not)z%(py0)s is not %(py3)sr4   r5   u2   Lv.2 + 미기재 시 경고가 반환되어야 함r7   r8   u    경고는 문자열이어야 함z7
>assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstancestr)r   rA   r   r   )r   r    r!   r"   r#   r$   r%   r&   r'   r(   r   r   )	r)   r[   r4   r9   r*   r:   r;   r   r,   s	            r-   $test_warn_missing_af_level2_no_fileszATestWarnMissingAffectedFiles.test_warn_missing_af_level2_no_files$  s	   d	-iAF!WvT!WWWvTWWWWWWvWWWvWWWTWWW#WWWWWWW&#&J&JJ(JJJJJJJzJJJzJJJJJJ&JJJ&JJJJJJ#JJJ#JJJ&JJJJJJr/   c                    d}t        |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  }t        j                  d	|       d
z   d|iz  }t        t        j                  |            dx}}y)u%   Lv.1 → None 반환 (경고 없음).u=   # task-101: 수정
레벨: Lv.1
작업 내용: 단순 수정.   r   Nisz%(py0)s is %(py3)sr4   r5   u'   Lv.1에서는 경고가 없어야 함: r7   r8   
r   r    r!   r"   r#   r$   r%   r&   r'   r(   rZ   s          r-   test_warn_missing_af_level1z8TestWarnMissingAffectedFiles.test_warn_missing_af_level1+  s{    U	-iAFQv~QQQvQQQQQQvQQQvQQQQQQ!HQQQQQQQr/   c                    d}t        |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  }t        j                  d	|       d
z   d|iz  }t        t        j                  |            dx}}y)u=   Lv.2 + affected_files 기재 → None 반환 (경고 없음).un   # task-102: 수정
레벨: Lv.2
affected_files: server.py, app.js
작업 내용: server.py를 수정합니다.rJ   r   Nr   r   r4   r5   u5   affected_files가 기재되면 경고 없어야 함: r7   r8   r   rZ   s          r-   &test_warn_missing_af_level2_with_fileszCTestWarnMissingAffectedFiles.test_warn_missing_af_level2_with_files1  s    ; 	 .iAF_v~___v______v___v______!VW]V^_______r/   N)rP   rQ   rR   r   r   r   rS   r/   r-   r   r   #  s    KR	`r/   r   c                   2    e Zd ZdededefdZd Zd Zd Zy)	TestCheckBatchCompletionrv   rw   rx   c                     |dz  }|j                  dd       |dz  }|j                  t        j                  d|idd      d	
       |S rz   r   r   s        r-   r   z)TestCheckBatchCompletion._make_timer_fileC  r   r/   c                 L   ddl }d}d|dd|dd}| j                  ||       |j                  |d|       t        |      }|d   }d	}||u }	|	st	        j
                  d
|	fd||f      t	        j                  |      t	        j                  |      dz  }
t	        j                  d|       dz   d|
iz  }t        t	        j                  |            dx}x}	}|d   }d}||k(  }	|	st	        j
                  d|	fd||f      t	        j                  |      t	        j                  |      dz  }
t	        j                  d|       dz   d|
iz  }t        t	        j                  |            dx}x}	}|d   }d}||k(  }	|	st	        j
                  d|	fd||f      t	        j                  |      t	        j                  |      dz  }
t	        j                  d|       dz   d|
iz  }t        t	        j                  |            dx}x}	}|d   }g }||k(  }	|	st	        j
                  d|	fd||f      t	        j                  |      t	        j                  |      dz  }
t	        j                  d|       dz   d|
iz  }t        t	        j                  |            dx}x}	}y)uB   모든 task가 done 상태이면 complete=True, pending=[] 반환.r   Nzbatch-20260415-testdoner   batch_id)ztask-301ztask-302r   completeTr   z%(py1)s is %(py4)srA   r   u+   모두 완료 시 complete=True여야 함: 
>assert %(py6)sr   totalrJ   r1   z%(py1)s == %(py4)su   total=2여야 함: u   done=2여야 함: pendingu   pending=[]이어야 함: 
r   r   r   r   r    r!   r%   r&   r'   r(   )r)   rv   r   r   r   rw   r4   rG   r   r9   r,   r   s               r-   test_batch_completion_all_donez7TestCheckBatchCompletion.test_batch_completion_all_doneN  s   (#)x@#)x@
 	h.Hk8<'1j!aTa!T)aaa!Taaa!aaaTaaa-XY_X`+aaaaaaaagC!C!#CCC!CCCCCC!CCC':6(%CCCCCCCCf~AA~"AAA~AAA~AAAAAA&8$AAAAAAAAi LBL B&LLL BLLL LLLBLLL*CF8(LLLLLLLLr/   c                 ,   ddl }d}d|dd|dd|dd}| j                  ||       |j                  |d|       t        |      }|d	   }d
}||u }	|	st	        j
                  d|	fd||f      t	        j                  |      t	        j                  |      dz  }
t	        j                  d|       dz   d|
iz  }t        t	        j                  |            dx}x}	}|d   }d}||k(  }	|	st	        j
                  d|	fd||f      t	        j                  |      t	        j                  |      dz  }
t	        j                  d|       dz   d|
iz  }t        t	        j                  |            dx}x}	}|d   }d}||k(  }	|	st	        j
                  d|	fd||f      t	        j                  |      t	        j                  |      dz  }
t	        j                  d|       dz   d|
iz  }t        t	        j                  |            dx}x}	}|d   }t        |      }ddg}||k(  }|st	        j
                  d|fd||f      dt        j                         v st	        j                  t              rt	        j                  t              ndt	        j                  |      t	        j                  |      t	        j                  |      dz  }t	        j                  d|       dz   d |iz  }t        t	        j                  |            dx}x}x}}y)!uZ   일부만 done 상태이면 complete=False, pending에 미완료 task가 있어야 한다.r   Nzbatch-20260415-partialr   r   r   )ztask-401task-402task-403r   r   Fr   r   r   u/   일부만 완료 시 complete=False여야 함: r   r   r      r1   r   u   total=3이어야 함: r   u   done=1이어야 함: r   r   r   )z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py7)ssorted)r   r   r   py7u   pending 목록 불일치: z
>assert %(py9)spy9)r   r   r   r   r    r!   r%   r&   r'   r(   r   r"   r#   r$   )r)   rv   r   r   r   rw   r4   rG   r   r9   r,   r   r*   @py_assert6r   @py_format8@py_format10s                    r-   test_batch_completion_partialz6TestCheckBatchCompletion.test_batch_completion_partial`  s%   +#)x@#,(C#,(C

 	h.Hk8<'1j!fUf!U*fff!Ufff!fffUfff.]^d]e,ffffffffgF!F!#FFF!FFFFFF!FFF'=fX%FFFFFFFFf~DD~"DDD~DDD~DDDDDD&;F8$DDDDDDDDY'kv'(kZ,Dk(,DDkkk(,Dkkkkkkvkkkvkkk'kkk(kkk,DkkkHbcibjFkkkkkkkkr/   c                    ddl }ddddi}| j                  ||       |j                  |d|       t        d      }|d	   }d}||k(  }|st	        j
                  d
|fd||f      t	        j                  |      t	        j                  |      dz  }	t	        j                  d|       dz   d|	iz  }
t        t	        j                  |
            dx}x}}|d   }d}||u }|st	        j
                  d|fd||f      t	        j                  |      t	        j                  |      dz  }	t	        j                  d|       dz   d|	iz  }
t        t	        j                  |
            dx}x}}y)uJ   batch_id가 일치하는 task가 없으면 total=0, complete=False 반환.r   Nztask-501r   zother-batchr   r   znonexistent-batch-idr   r1   r   r   u2   매칭되는 task 없으면 total=0이어야 함: r   r   r   Fr   r   u(   total=0이면 complete=False여야 함: r   )r)   rv   r   r   rw   r4   rG   r   r9   r,   r   s              r-   test_batch_completion_no_matchz7TestCheckBatchCompletion.test_batch_completion_no_matchs  s    6}E
 	h.Hk8<'(>?gb!b!#bbb!bbbbbb!bbb'YZ`Ya%bbbbbbbbj!_U_!U*___!U___!___U___.VW]V^,________r/   N)	rP   rQ   rR   r   r   r   r   r   r   rS   r/   r-   r   r   B  s.    	 	d 	t 	M$l&`r/   r   c                   $    e Zd Zd Zd Zd Zd Zy)TestEstimateTaskLevelc                    g d}t        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  }t        j                  d| d	|       d
z   d|iz  }t        t        j                  |            dx}}|s{t        j                  d      dz   ddt        j                         v st        j
                  |      rt        j                  |      ndiz  }t        t        j                  |            y)u+   affected_files 5개 → 최소 Lv.2 권장.)za.pyzb.pyzc.pyzd.pyze.pyu   일반 작업rJ   >=z%(py0)s >= %(py3)sr   r5   u9   5개 파일이면 Lv.2 이상 권장이어야 함: level=	, reason=r7   r8   Nu   이유가 있어야 함z
>assert %(py0)sr   reason
r   r    r!   r"   r#   r$   r%   r&   r'   r(   )	r)   filesr   r   r9   r*   r:   r;   @py_format1s	            r-   test_estimate_level_many_filesz4TestEstimateTaskLevel.test_estimate_level_many_files  s    8,_eDvouzooouoooooouooouooooooVW\V]]fgmfnooooooo000000000v000v00000vr/   c                 (   dg}t        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  }t        j                  d| 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)u(   server.py 포함 → 최소 Lv.2 권장.rW   u   서버 수정rJ   r   r   r   r5   u<   server.py 포함 시 Lv.2 이상 권장이어야 함: level=r   r7   r8   Nr   rC   r   r@   u.   이유에 'server.py'가 포함되어야 함: r   )	r)   r   r   r   r9   r*   r:   r;   rG   s	            r-   test_estimate_level_server_pyz3TestEstimateTaskLevel.test_estimate_level_server_py  s    ,_eDvruzrrrurrrrrrurrrurrrrrrYZ_Y``ijpiqrrrrrrr_{f$___{f___{______f___f____(VW]V^&_______r/   c                    d}t        |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  }t        j                  d| d|       d	z   d
|iz  }t        t        j                  |            dx}}y)u7   '아키텍처' 키워드 포함 → 최소 Lv.3 권장.u;   전체 아키텍처를 변경하는 대형 작업입니다.r   r   r   r   r5   uD   '아키텍처' 키워드 시 Lv.3 이상 권장이어야 함: level=r   r7   r8   Nr   )r)   r[   r   r   r9   r*   r:   r;   s           r-   (test_estimate_level_architecture_keywordz>TestEstimateTaskLevel.test_estimate_level_architecture_keyword  s    Q	,Y;vzuzzzzuzzzzzzuzzzuzzzzzzabgahhqrxqyzzzzzzzr/   c                 .   d}dg}t        ||      \  }}d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dz  }t        j                  d| d	|       d
z   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  }t        j                  d|      d
z   d|iz  }t        t        j                  |            dx}}y)u0   파일 1개, 키워드 없음 → Lv.1 (기본).u   단순 오타 수정z
readme.txtr   r1   r3   r   r5   u*   단순 작업은 Lv.1이어야 함: level=r   r7   r8   N r   u1   단순 작업은 이유가 없어야 함: reason=r   )	r)   r[   r   r   r   r9   r*   r:   r;   s	            r-   test_estimate_level_simplez0TestEstimateTaskLevel.test_estimate_level_simple  s    *	,Y>v`uz```u``````u```u``````GwiX^W_```````[v|[[[v[[[[[[v[[[v[[[[[[PQWPZ[[[[[[[r/   N)rP   rQ   rR   r   r   r   r   rS   r/   r-   r   r     s    1`{\r/   r   c                       e Zd Zd Zd Zy)TestParseTaskLevelc                    d}t        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dz  }t        j                  d|       dz   d	|iz  }t        t        j                  |            d
x}}y
)u%   'Lv.2' 포함 task_desc → 2 반환.uD   # task-100: 수정
레벨: Lv.2
작업 내용: 중간 규모 수정.rJ   r1   r3   r4   r5   u   Lv.2 파싱 실패: r7   r8   N
r
   r    r!   r"   r#   r$   r%   r&   r'   r(   rZ   s          r-   test_parse_task_level_lv2z,TestParseTaskLevel.test_parse_task_level_lv2  sy    \	"9-;v{;;;v;;;;;;v;;;v;;;;;;26(;;;;;;;r/   c                    d}t        |      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dz  }t        j                  d|       dz   d	|iz  }t        t        j                  |            d
x}}y
)u,   레벨 표기 없음 → 기본값 1 반환.u<   # task-101: 수정
레벨 표기가 없는 간단한 작업.r   r1   r3   r4   r5   u,   레벨 없을 때 기본값 1이어야 함: r7   r8   Nr   rZ   s          r-   test_parse_task_level_defaultz0TestParseTaskLevel.test_parse_task_level_default  sy    S	"9-Sv{SSSvSSSSSSvSSSvSSSSSSJ6(SSSSSSSr/   N)rP   rQ   rR   r   r   rS   r/   r-   r   r     s    <Tr/   r   c                   (    e Zd ZdZd Zd Zd Zd Zy)TestAstBlastRadiusu'   _get_ast_blast_radius 함수 테스트.c                    t        dgd      }|d   }g }||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t	        t        j
                  |            d	x}x}}|d
   }d}||k(  }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t	        t        j
                  |            d	x}x}}y	)u6   py 확장자 없는 affected_files면 빈 dict 반환.z	README.md/tmpdirect_importersr1   r   r   assert %(py6)sr   Ntotal_affectedr   )r   r    r!   r%   r'   r(   r)   r4   rG   r   r9   r,   r   s          r-   #test_returns_empty_when_no_py_filesz6TestAstBlastRadius.test_returns_empty_when_no_py_files  s    &}f=()/R/)R////)R///)///R///////&',1,'1,,,,'1,,,',,,1,,,,,,,r/   c                    t        j                  dddgg dgddd      }t               }d|_        ||_        d	|_        t        d
|      5  t        dgd      }ddd       d}d   }||v }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}d}|d   }||v }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y# 1 sw Y   	xY w)u.   AST 호출 성공 시 direct_importers 반환.data_loader.pyrW   z	routes.pyztest_data_loader.pyr   )r   callers
test_filesr   )changed_fileblast_radiusr   r   subprocess.runreturn_valuer   Nr   r   )z%(py1)s in %(py4)sr   r   r   )r   r   r   
returncodestdoutstderrr   r   r    r!   r%   r'   r(   )	r)   
ast_output	mock_procr4   rG   r   r9   r,   r   s	            r-   !test_returns_importers_on_successz4TestAstBlastRadius.test_returns_importers_on_success  s.   ZZ,%0+$>45"#	!
 
 K	 	%		#)< 	G*,<+=vFF	G 8f%788{88888{8888{888888888888f%788{88888{8888{88888888888		G 	Gs   E""E,c                    t        dt        j                  g d            5  t        dgd      }ddd       d   }g }||k(  }|slt	        j
                  d	|fd
||f      t	        j                  |      t	        j                  |      dz  }dd|iz  }t        t	        j                  |            dx}x}}y# 1 sw Y   xY w)u%   AST 타임아웃 시 빈 dict 반환.r     )cmdtimeout)side_effectr  r   Nr   r1   r   r   r   r   )	r   
subprocessTimeoutExpiredr   r    r!   r%   r'   r(   r   s          r-   test_returns_empty_on_timeoutz0TestAstBlastRadius.test_returns_empty_on_timeout  s    #1J1Jr[]1^_ 	G*,<+=vFF	G ()/R/)R////)R///)///R///////	G 	Gs   B::Cc                    t               }d|_        d|_        d|_        t	        d|      5  t        dgd      }ddd       d	   }g }||k(  }|slt        j                  d
|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y# 1 sw Y   xY w)u,   AST 스크립트 실패 시 빈 dict 반환.r   r   errorr  r  r  r   Nr   r1   r   r   r   r   )r   r
  r  r  r   r   r    r!   r%   r'   r(   )r)   r  r4   rG   r   r9   r,   r   s           r-   "test_returns_empty_on_script_errorz5TestAstBlastRadius.test_returns_empty_on_script_error  s    K	 		"	#)< 	G*,<+=vFF	G ()/R/)R////)R///)///R///////	G 	Gs   CCN)rP   rQ   rR   __doc__r   r  r  r  rS   r/   r-   r   r     s    1-9.0
0r/   r   )*r  builtinsr"   _pytest.assertion.rewrite	assertionrewriter    r   r  syspathlibr   unittest.mockr   r   pytest__file__parent
_WORKSPACEpathinsertr   r   r   r   r   r	   r
   r   r   prompts.gate_instructionsr   r   r   r   rU   ru   r   r   r   r   r   rS   r/   r-   <module>r*     s       
  *  (^""))
 3z? #    w  wPsc scvG\ G\^` `>=` =`J\ \FT T"10 10r/   