
    if'              	       (   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 e ee      j&                  j&                               ddlmZ ddlmZ ddeded	ed
efdZ G d d      Z G d d      Zy)u   카마소츠(테스터): AST 통합 테스트 — worktree_manager & codex_gate_check.

대상 함수:
  - worktree_manager._get_blast_radius_summary
  - codex_gate_check._get_callers_context
    N)Path)	MagicMockpatch)_get_blast_radius_summary)_get_callers_context
returncodestdoutstderrreturnc                 D    t               }| |_        ||_        ||_        |S )u%   subprocess.run 반환값 Mock 생성.)r   r   r	   r
   )r   r	   r
   mocks       9/home/jay/workspace/scripts/tests/test_ast_integration.py
_make_procr      s#    ;D DODKDKK    c                   :    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
y	)
TestGetBlastRadiusSummaryu<   worktree_manager._get_blast_radius_summary 유닛 테스트.c                    t        dd      }t        d|      5  t        ddd      }d	d	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)u5   git diff returncode != 0 이면 빈 문자열 반환.   zfatal: not a git repor   r
   subprocess.runreturn_value
/fake/pathfeaturemainN ==z%(py0)s == %(py3)sresultpy0py3assert %(py5)spy5r   r   r   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanation)selfgit_failr    @py_assert2@py_assert1@py_format4@py_format6s          r   #test_git_diff_failure_returns_emptyz=TestGetBlastRadiusSummary.test_git_diff_failure_returns_empty*   s    3JK#(; 	P.|YOF	P v|vvv	P 	P   CC!c                    t        dd      }t        d|      5  t        ddd      }d	d	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)u7   변경된 .py 파일이 없으면 빈 문자열 반환.r   r   r   r	   r   r   r   r   r   Nr   r   r    r!   r$   r%   r&   )r/   git_okr    r1   r2   r3   r4   s          r   #test_no_changed_files_returns_emptyz=TestGetBlastRadiusSummary.test_no_changed_files_returns_empty3   s    q4#&9 	P.|YOF	P v|vvv	P 	Pr6   c                 ,   t        dd      }dgdgd}t        dt        j                  |            }t        d||g      5  t	        d	d
d      }ddd       j
                  }d} ||      }|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}}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)u5   AST 성공 시 '## Blast Radius' 마크다운 반환.r   zsrc/foo.py
src/bar.py
r8   z
src/baz.pyztests/test_foo.pydirect_importers
test_filesr   side_effectr   r   r   N## Blast RadiusLassert %(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.startswith
}(%(py4)s)
}r    r"   py2py4py6u   변경 파일inz%(py1)s in %(py3)spy1r#   r$   r%   u   영향받는 파일u   관련 테스트r   jsondumpsr   r   
startswithr)   r*   r'   r+   r,   r-   r.   r(   r/   r9   ast_dataast_okr    r2   @py_assert3@py_assert5@py_format7@py_assert0r1   r3   r4   s                r   .test_ast_success_returns_blast_radius_markdownzHTestGetBlastRadiusSummary.test_ast_success_returns_blast_radius_markdown<   s   q1KL!-./
 qH1EF#&&1AB 	P.|YOF	P   3!23 !233333333v333v333 333!23333333333(&((((&(((((((((&(((&((((((($.$....$...$................!+!V++++!V+++!++++++V+++V+++++++	P 	Ps   L		Lc                    t        dd      }g g d}t        dt        j                  |            }t        d||g      5  t	        ddd	      }d
d
d
       j
                  }d} ||      }|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}}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   oxY w)uN   AST 성공이지만 direct_importers/test_files 비어도 마크다운 반환.r   src/foo.py
r8   r<   r   r?   r   r   r   NrA   rB   r    rC   u   없음rG   rI   rJ   r$   r%   rL   rP   s                r    test_ast_success_empty_importersz:TestGetBlastRadiusSummary.test_ast_success_empty_importersM   s2   q@(*"=qH1EF#&&1AB 	P.|YOF	P   3!23 !233333333v333v333 333!23333333333!x6!!!!x6!!!x!!!!!!6!!!6!!!!!!!		P 	Ps   F==Gc                    t        dt        j                  dd            5  t        ddd      }d	d	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:   subprocess.TimeoutExpired 발생 시 빈 문자열 반환.r   git   cmdtimeoutr?   r   r   r   Nr   r   r   r    r!   r$   r%   )r   
subprocessTimeoutExpiredr   r'   r(   r)   r*   r+   r,   r-   r.   r/   r    r1   r2   r3   r4   s         r   %test_subprocess_timeout_returns_emptyz?TestGetBlastRadiusSummary.test_subprocess_timeout_returns_emptyY   s    #1J1Ju^`1ab 	P.|YOF	P v|vvv	P 	Ps   C  C)c                    t        dd      }t        dd      }t        d||g      5  t        ddd	      }d
d
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)uB   AST stdout이 유효하지 않은 JSON이면 빈 문자열 반환.r   rY   r8   zNOT VALID JSON {{{r   r?   r   r   r   Nr   r   r   r    r!   r$   r%   r&   )r/   r9   ast_badr    r1   r2   r3   r4   s           r   %test_json_parse_failure_returns_emptyz?TestGetBlastRadiusSummary.test_json_parse_failure_returns_empty`   s    q@2FG#&'1BC 	P.|YOF	P v|vvv	P 	P   C''C0c                    t        dd      }t        dd      }t        d||g      5  t        d	d
d      }dd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=   AST 스크립트 returncode != 0 이면 빈 문자열 반환.r   rY   r8   r   zError: script failedr   r   r?   r   r   r   Nr   r   r   r    r!   r$   r%   r&   )r/   r9   ast_failr    r1   r2   r3   r4   s           r   %test_ast_script_failure_returns_emptyz?TestGetBlastRadiusSummary.test_ast_script_failure_returns_emptyj   s    q@3IJ#&(1CD 	P.|YOF	P v|vvv	P 	Prh   N)__name__
__module____qualname____doc__r5   r:   rW   rZ   rd   rg   rk    r   r   r   r   '   s(    F,"
"r   r   c                   @    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
d	 Zy
)TestGetCallersContextu7   codex_gate_check._get_callers_context 유닛 테스트.c                 h   t        g 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	)
u/   빈 affected_files 리스트 → 빈 문자열.z/fake/workspacer   r   r   r    r!   r$   r%   N)	r   r'   r(   r)   r*   r+   r,   r-   r.   rc   s         r   test_empty_list_returns_emptyz3TestGetCallersContext.test_empty_list_returns_empty}   sg    %b*;<v|vvvr   c                 |   t        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  }dd|iz  }t        t        j                  |            d	x}}y	)
u:   AST 스크립트 파일이 없으면 빈 문자열 반환.
src/foo.pyr   r   r   r    r!   r$   r%   N)
r   strr'   r(   r)   r*   r+   r,   r-   r.   )r/   tmp_pathr    r1   r2   r3   r4   s          r   'test_ast_script_not_found_returns_emptyz=TestGetCallersContext.test_ast_script_not_found_returns_empty   sn     &|nc(mDv|vvvr   c                 (   |dz  }|j                          |dz  j                          ddg dii}t        dt        j                  |            }t        d|	      5  t        d
gt        |            }ddd       j                  }d} ||      }|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}}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)uL   AST 성공 + callers 있음 → '함수 호출자 정보:' 포함 문자열.scriptsast_dependency_map.pyblast_radiuscallers)a.pyb.pyzc.pyr   r8   r   r   rv   N   함수 호출자 정보:rB   r    rC   rG   rI   rJ   r$   r%   u   3곳에서 호출됨)mkdirtouchr   rM   rN   r   r   rw   rO   r)   r*   r'   r+   r,   r-   r.   r(   )r/   rx   ast_dirrQ   rR   r    r2   rS   rT   rU   rV   r1   r3   r4   s                 r   *test_ast_success_with_callers_returns_infoz@TestGetCallersContext.test_ast_success_with_callers_returns_info   s    Y&	*	*113 3

 qH1EF#&9 	I)<.#h-HF	I   <!;< !;<<<<<<<<v<<<v<<< <<<!;<<<<<<<<<<%|v%%%%|v%%%|%%%%%%v%%%v%%%%%%%%/%////%///%////////////////	I 	Is   JJc                 \   |dz  }|j                          |dz  j                          ddg ii}t        dt        j                  |            }t        d|      5  t        d	gt        |            }d
d
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)u4   AST 성공 + callers 비어있음 → 빈 문자열.r{   r|   r}   r~   r   r8   r   r   rv   Nr   r   r   r    r!   r$   r%   )r   r   r   rM   rN   r   r   rw   r'   r(   r)   r*   r+   r,   r-   r.   )
r/   rx   r   rQ   rR   r    r1   r2   r3   r4   s
             r   )test_ast_success_no_callers_returns_emptyz?TestGetCallersContext.test_ast_success_no_callers_returns_empty   s    Y&	*	*113"YO4qH1EF#&9 	I)<.#h-HF	I v|vvv	I 	Is   D""D+c                 :   |dz  }|j                          |dz  j                          t        dt        j                  dd            5  t        dgt        |            }d	d	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(   TimeoutExpired 발생 시 빈 문자열.r{   r|   r   python3r]   r^   r?   rv   Nr   r   r   r    r!   r$   r%   )r   r   r   ra   rb   r   rw   r'   r(   r)   r*   r+   r,   r-   r.   )r/   rx   r   r    r1   r2   r3   r4   s           r   rd   z;TestGetCallersContext.test_subprocess_timeout_returns_empty   s    Y&	*	*113#1J1Jybd1ef 	I)<.#h-HF	I v|vvv	I 	Is   DDc                 J   |dz  }|j                          |dz  j                          t        d      D cg c]  }d| d
 }}dd|ii}t        dt	        j
                  |      	      }t        d
|      5  t        dgt        |            }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  }
dd|
iz  }t        t        j                   |            dx}}	|j#                  dd      d   }|j#                  d      D cg c]  }|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 # 1 sw Y   xY wc c}w )!u6   callers가 6개 이상이어도 상위 5개만 표시.r{   r|      caller_z.pyr}   r~   r   r8   r   r   rv   Nu   8곳에서 호출됨rG   rI   r    rJ   r$   r%   z:    ,   r   )z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenlisted)r"   rK   r#   rF   zassert %(py8)spy8)r   r   ranger   rM   rN   r   r   rw   r'   r(   r,   r)   r*   r+   r-   r.   splitstripr   )r/   rx   r   ir~   rQ   rR   r    rV   r1   r3   r4   caller_partcr   rT   @py_assert4rU   @py_format9s                      r   test_top_5_callers_onlyz-TestGetCallersContext.test_top_5_callers_only   s   Y&	*	*113-21X6WQCs#66"Y$89qH1EF#&9 	I)<.#h-HF	I &/%////%///%////////////////ll4+B/%0%6%6s%;<!'')<<6{a{a{ass66{a 7	I 	I =s   J8J$J Jc                    |dz  }|j                          |dz  j                          d }t        d|      5  t        ddgt	        |            }ddd       j
                  }d	} ||      }|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}}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)u=   여러 파일 처리 시 callers 있는 파일만 행 생성.r{   r|   c                     d| v r| j                  d      nd}|dk\  r| |dz      nd}d|v r	ddd	d
gii}nddg ii}t        dt        j                  |            S )N--filesr   r   r   foo.pyr}   r~   r   r   r8   indexr   rM   rN   r_   _kw	files_idxfile_argdatas        r   r@   zKTestGetCallersContext.test_multiple_files_with_callers.<locals>.side_effect   sq     1:S0@		),bI-6!^s9q=)H8#&VV4D(EF&B84::d3CDDr   r   r?   rv   
src/bar.pyNr   rB   r    rC   r   rG   rI   rJ   r$   r%   bar.py)not in)z%(py1)s not in %(py3)sr   r   r   r   rw   rO   r)   r*   r'   r+   r,   r-   r.   r(   r/   rx   r   r@   r    r2   rS   rT   rU   rV   r1   r3   r4   s                r    test_multiple_files_with_callersz6TestGetCallersContext.test_multiple_files_with_callers   s   Y&	*	*113		E #= 	W)<*FHVF	W   <!;< !;<<<<<<<<v<<<v<<< <<<!;<<<<<<<<<<!x6!!!!x6!!!x!!!!!!6!!!6!!!!!!!%xv%%%%xv%%%x%%%%%%v%%%v%%%%%%%	W 	Ws   I##I-c                    |dz  }|j                          |dz  j                          d }t        d|      5  t        ddgt	        |            }ddd       j
                  }d	} ||      }|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}}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   oxY w)u>   JSON 파싱 실패한 파일은 건너뛰고 나머지 반환.r{   r|   c                     d| v r| j                  d      nd}|dk\  r| |dz      nd}d|v rt        dd      S d	d
dgii}t        dt        j                  |            S )Nr   r   r   r   r   r   zINVALID JSONr8   r}   r~   zx.pyr   r   s        r   r@   zMTestGetCallersContext.test_json_parse_failure_skips_file.<locals>.side_effect   sk    09S0@		),bI-6!^s9q=)H8#!Q~FF"Y$9:D4::d3CDDr   r   r?   rv   r   Nr   rB   r    rC   r   rG   rI   rJ   r$   r%   r   r   s                r   "test_json_parse_failure_skips_filez8TestGetCallersContext.test_json_parse_failure_skips_file   s6   Y&	*	*113	E #= 	W)<*FHVF	W   <!;< !;<<<<<<<<v<<<v<<< <<<!;<<<<<<<<<<!x6!!!!x6!!!x!!!!!!6!!!6!!!!!!!	W 	Ws   F>>GN)rl   rm   rn   ro   rt   ry   r   r   rd   r   r   r   rp   r   r   rr   rr   z   s-    A
0*	 (&0"r   rr   )r   r   r   )ro   builtinsr)   _pytest.assertion.rewrite	assertionrewriter'   rM   ra   syspathlibr   unittest.mockr   r   pathinsertrw   __file__parentworktree_managerr   codex_gate_checkr   intr   r   rr   rp   r   r   <module>r      s       
  * 3tH~,,334 5 6 13 C c 9 K Kf{" {"r   