
    $im                        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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             edz  Zej2                  j5                  de      ZdZeeuZes ej<                  defdeef      d	 ej>                         v s ej@                  e      r ejB                  e      nd	 ejB                  e      d
z  Z"dde"iz  Z# e$ ejJ                  e#            dxZZej2                  jM                  e      Z'ejP                  ZdZ)ee)uZ*e*s ej<                  de*fdee)f      d	 ej>                         v s ej@                  e      r ejB                  e      nd	 ejB                  e       ejB                  e)      dz  Z#dde#iz  Z+ e$ ejJ                  e+            dxZxZ*Z)ejP                  jY                  e'        G d d      Z- G d d      Z. G d d      Z/ G d d      Z0 G d d      Z1 G d d      Z2 G d d      Z3 G d d       Z4 G d! d"      Z5 G d# d$      Z6y)%u  
test_code_review.py

scripts/code-review.py 단위 테스트 (TDD)

테스트 항목:
1. check_hardcoded_secrets - 시크릿 탐지
2. check_todos - TODO/FIXME 탐지
3. check_unused_imports - 미사용 import 탐지
4. check_function_length - 과도한 함수 길이 탐지
5. review_file - 파일 종합 리뷰
6. review_all - 전체 리뷰 결과 집계
7. get_changed_files - git diff 기반 파일 목록
8. 에지 케이스: 빈 파일, 바이너리 파일, git 불가 환경
9. CLI 출력 형식
    N)Path)	MagicMockpatchzcode-review.pycode_review)is not)z%(py0)s is not %(py3)sspecpy0py3assert %(py5)spy5)z2%(py2)s
{%(py2)s = %(py0)s.loader
} is not %(py5)s)r
   py2r   zassert %(py7)spy7c                   X    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
d	 Zd
 Zd Zd Zd Zy)TestCheckHardcodedSecretsu*   하드코딩된 시크릿 탐지 테스트c                    d}t         j                  d|      }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}}d |D        }t        |      }	|	sd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}}	d |D        }t        |      }	|	sd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)u   AWS Access Key ID 탐지z*AWS_ACCESS_KEY_ID = "AKIAIOSFODNN7EXAMPLE"test.py   >=z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} >= %(py6)slenfindingsr
   py1r   py6assert %(py8)spy8Nc              3   ,   K   | ]  }|d    dk(    yw)severitycriticalN .0fs     5/home/jay/workspace/scripts/tests/test_code_review.py	<genexpr>zHTestCheckHardcodedSecrets.test_detects_aws_access_key.<locals>.<genexpr>5   s     A11Z=J.A   ,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}anyr
   r   py4c              3   ,   K   | ]  }|d    dk(    yw)categoryhardcoded_secretNr"   r#   s     r&   r'   zHTestCheckHardcodedSecrets.test_detects_aws_access_key.<locals>.<genexpr>6   s     I11Z=$66Ir(   )r   check_hardcoded_secretsr   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanationr*   )selfcontentr   @py_assert2@py_assert5@py_assert4@py_format7@py_format9@py_assert1@py_assert3@py_format5s              r&   test_detects_aws_access_keyz5TestCheckHardcodedSecrets.test_detects_aws_access_key0   sc   >66y'J8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!AAAsAAAAAAAAAsAAAsAAAAAAAAAAAAAAIIIsIIIIIIIIIsIIIsIIIIIIIIIIIIII    c                 Z   d}t         j                  d|      }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}}|d   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)u   api_key = '...' 패턴 탐지zapi_key = 'sk-1234567890abcdef'r   r   r   r   r   r   r   r   r   Nr   r    r!   ==z%(py1)s == %(py4)sr   r,   assert %(py6)sr   r   r0   r   r1   r2   r3   r4   r5   r6   r7   r8   r9   r:   r   r;   r<   r=   r>   r?   @py_assert0rA   rB   s              r&   test_detects_api_key_assignmentz9TestCheckHardcodedSecrets.test_detects_api_key_assignment8   s    366y'J8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!{:&4*4&*4444&*444&444*4444444rD   c                 T   d}t         j                  d|      }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   password = '...' 패턴 탐지z&password = "super_secret_password_123"r   r   r   r   r   r   r   r   r   NrK   r9   r:   r   r;   r<   r=   r>   r?   s           r&    test_detects_password_assignmentz:TestCheckHardcodedSecrets.test_detects_password_assignment?   s    :66y'J8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   c                 T   d}t         j                  d|      }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   token = '...' 패턴 탐지z"token = "ghp_1234567890abcdefghij"r   r   r   r   r   r   r   r   r   NrK   rP   s           r&   test_detects_token_assignmentz7TestCheckHardcodedSecrets.test_detects_token_assignmentE       666y'J8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   c                 T   d}t         j                  d|      }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    secret_key = '...' 패턴 탐지z"secret_key = "my-super-secret-key"r   r   r   r   r   r   r   r   r   NrK   rP   s           r&   "test_detects_secret_key_assignmentz<TestCheckHardcodedSecrets.test_detects_secret_key_assignmentK   rT   rD   c                 T   d}t         j                  d|      }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%   환경변수 참조는 탐지 안 함z#api_key = os.environ.get("API_KEY")r   r   rF   z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)sr   r   r   r   r   NrK   rP   s           r&   test_no_false_positive_env_varz8TestCheckHardcodedSecrets.test_no_false_positive_env_varQ       766y'J8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   c                 T   d}t         j                  d|      }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&   빈 문자열 할당은 탐지 안 함zpassword = ""r   r   rF   rX   r   r   r   r   r   NrK   rP   s           r&   #test_no_false_positive_empty_stringz=TestCheckHardcodedSecrets.test_no_false_positive_empty_stringW   s    !66y'J8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   c                 Z   d}t         j                  d|      }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}}|d   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)"   발견 항목에 줄 번호 포함z'# line 1
# line 2
api_key = 'secret123'r   r   r   r   r   r   r   r   r   Nr   line   rF   rH   rI   rJ   r   rK   rL   s              r&   test_finding_has_line_numberz6TestCheckHardcodedSecrets.test_finding_has_line_number]   s    =66y'J8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!{6"'a'"a''''"a'''"'''a'''''''rD   c                 :   d}t         j                  d|      }|d   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
)u%   발견 항목에 파일 경로 포함zpassword = "secret"zscripts/test.pyr   filerF   rH   rI   rJ   r   N)r   r0   r1   r2   r6   r7   r8   r9   r:   r   rM   rA   r;   rB   r>   s           r&   test_finding_has_file_pathz4TestCheckHardcodedSecrets.test_finding_has_file_pathd   st    '667H'R{6"7&77"&77777"&7777"777&77777777rD   c                 |   t         j                  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  }dd|iz  }t        t        j                  |            d	x}}y	
u   빈 파일에서 탐지 없음r    rF   z%(py0)s == %(py3)sr   r	   r   r   N)
r   r0   r1   r2   r3   r4   r5   r6   r7   r8   r9   r   r;   r@   @py_format4@py_format6s         r&   &test_empty_content_returns_no_findingsz@TestCheckHardcodedSecrets.test_empty_content_returns_no_findingsj   sl    66y"Ex2~x2xx2rD   c                 T   d}t         j                  d|      }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)   -----BEGIN PRIVATE KEY----- 헤더 탐지z#key = '-----BEGIN PRIVATE KEY-----'r   r   r   r   r   r   r   r   r   NrK   rP   s           r&   test_detects_private_key_headerz9TestCheckHardcodedSecrets.test_detects_private_key_headero   rZ   rD   c                 T   d}t         j                  d|      }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,   여러 줄에 걸쳐 있는 시크릿 탐지zHx = 1
y = 2
api_key = "AKIAIOSFODNN7EXAMPLE"
z = 3
password = "hunter2"
r      r   r   r   r   r   r   r   NrK   rP   s           r&   test_multiline_detectionz2TestCheckHardcodedSecrets.test_multiline_detectionu   s    m66y'J8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   N)__name__
__module____qualname____doc__rC   rN   rQ   rS   rV   rY   r\   ra   re   rm   ro   rr   r"   rD   r&   r   r   -   sB    4J5"""""(8
""rD   r   c                   R    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
d	 Zd
 Zd Zd Zy)TestCheckTodosu"   TODO/FIXME 잔존 탐지 테스트c                 `   d}t         j                  d|      }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}}|d   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}}	|d   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)u   # TODO: 패턴 탐지# TODO: fix this laterr   r   rF   rX   r   r   r   r   r   Nr   r.   todorH   rI   rJ   r   r    infor   check_todosr   r1   r2   r3   r4   r5   r6   r7   r8   rL   s              r&   test_detects_todo_commentz(TestCheckTodos.test_detects_todo_comment   sQ   ***9g>8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!{:&0&0&&0000&&000&000&0000000{:&0&0&&0000&&000&000&0000000rD   c                 Z   d}t         j                  d|      }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}}|d   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)u   # FIXME: 패턴 탐지z# FIXME: broken logic herer   r   rF   rX   r   r   r   r   r   Nr   r.   r{   rH   rI   rJ   r   r}   rL   s              r&   test_detects_fixme_commentz)TestCheckTodos.test_detects_fixme_comment   s    .**9g>8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!{:&0&0&&0000&&000&000&0000000rD   c                 T   d}t         j                  d|      }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   인라인 TODO 탐지zx = 1  # TODO: remove thisr   r   rF   rX   r   r   r   r   r   Nr}   rP   s           r&   test_detects_todo_inlinez'TestCheckTodos.test_detects_todo_inline   s    .**9g>8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   c                 T   d}t         j                  d|      }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   # HACK: 패턴 탐지z# HACK: temporary workaroundr   r   r   r   r   r   r   r   r   Nr}   rP   s           r&   test_detects_hack_commentz(TestCheckTodos.test_detects_hack_comment   s    0**9g>8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   c                 T   d}t         j                  d|      }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   # XXX: 패턴 탐지z# XXX: this needs attentionr   r   r   r   r   r   r   r   r   Nr}   rP   s           r&   test_detects_xxx_commentz'TestCheckTodos.test_detects_xxx_comment   s    /**9g>8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   c                 T   d}t         j                  d|      }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   대소문자 무관 탐지z# todo: lowercase todor   r   r   r   r   r   r   r   r   Nr}   rP   s           r&   test_case_insensitive_todoz)TestCheckTodos.test_case_insensitive_todo   s    ***9g>8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   c                 8   d}t         j                  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}}y)u&   발견 메시지에 TODO 내용 포함rz   r   zfix this laterr   messageinz%(py1)s in %(py4)srI   rJ   r   Nr   r~   r1   r2   r6   r7   r8   rd   s           r&   'test_finding_message_contains_todo_textz6TestCheckTodos.test_finding_message_contains_todo_text   sr    ***9g>98A;y#99#99999#9999999#99999999rD   c                 :   d}t         j                  d|      }|d   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)u   줄 번호 정확성zx = 1
# TODO: check
y = 2r   r   r_   rq   rF   rH   rI   rJ   r   Nr   rd   s           r&    test_finding_line_number_correctz/TestCheckTodos.test_finding_line_number_correct   so    /**9g>{6"'a'"a''''"a'''"'''a'''''''rD   c                 T   d}t         j                  d|      }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   여러 TODO 탐지z+# TODO: first
# FIXME: second
# TODO: thirdr   r`   rF   rX   r   r   r   r   r   Nr}   rP   s           r&   test_multiple_todos_detectedz+TestCheckTodos.test_multiple_todos_detected   s    A**9g>8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   c                 |   t         j                  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  }dd|iz  }t        t        j                  |            d	x}}y	rg   
r   r~   r1   r2   r3   r4   r5   r6   r7   r8   rj   s         r&   rm   z5TestCheckTodos.test_empty_content_returns_no_findings   sl    **9b9x2~x2xx2rD   c                    d}t         j                  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  }dd|iz  }t        t        j                  |            d	x}}y	)
u!   일반 코드에서 오탐 없음z8message = 'This is working correctly'
result = compute()r   rF   ri   r   r	   r   r   Nr   r9   r:   r   r;   r@   rk   rl   s          r&    test_no_false_positive_in_stringz/TestCheckTodos.test_no_false_positive_in_string   sq    M**9g>x2~x2xx2rD   N)rs   rt   ru   rv   r   r   r   r   r   r   r   r   r   rm   r   r"   rD   r&   rx   rx      s<    ,11"""":("
rD   rx   c                   X    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
d	 Zd
 Zd Zd Zd Zy)TestCheckUnusedImportsu!   미사용 import 탐지 테스트c                    d}t         j                  d|      }d |D        }t        |      }|sd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)u   미사용 import 탐지z%import os
import sys

print(sys.argv)r   c              3   *   K   | ]  }d |d   v   yw)osr   Nr"   r#   s     r&   r'   zDTestCheckUnusedImports.test_detects_unused_import.<locals>.<genexpr>   s     :A41Y<':   r)   r*   r+   N
r   check_unused_importsr*   r3   r4   r1   r5   r6   r7   r8   r9   r:   r   r@   rA   rB   s         r&   test_detects_unused_importz1TestCheckUnusedImports.test_detects_unused_import   sx    <33IwG:::s:::::::::s:::s::::::::::::::rD   c                 T   d}t         j                  d|      }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"   사용된 import는 탐지 안 함z*import os

result = os.path.join('a', 'b')r   r   rF   rX   r   r   r   r   r   Nr   r   r   r1   r2   r3   r4   r5   r6   r7   r8   rP   s           r&   test_no_finding_for_used_importz6TestCheckUnusedImports.test_no_finding_for_used_import   s    @33IwG8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   c                 T   d}t         j                  d|      }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   여러 미사용 import 탐지z'import os
import sys
import json

x = 1r   rq   r   r   r   r   r   r   r   Nr   rP   s           r&   $test_detects_multiple_unused_importsz;TestCheckUnusedImports.test_detects_multiple_unused_imports   s    ?33IwG8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   c                    d}t         j                  d|      }d |D        }t        |      }|sd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)u%   미사용 import 심각도는 warningimport os

x = 1r   c              3   ,   K   | ]  }|d    dk(    yw)r    warningNr"   r#   s     r&   r'   zJTestCheckUnusedImports.test_finding_severity_is_warning.<locals>.<genexpr>   s     @!1Z=I-@r(   r)   allr+   N
r   r   r   r3   r4   r1   r5   r6   r7   r8   r   s         r&    test_finding_severity_is_warningz7TestCheckUnusedImports.test_finding_severity_is_warning   sx    &33IwG@x@@s@@@@@@@@@s@@@s@@@@@@@@@@@@@@rD   c                    d}t         j                  d|      }d |D        }t        |      }|sd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)u   발견 카테고리 확인r   r   c              3   ,   K   | ]  }|d    dk(    yw)r.   unused_importNr"   r#   s     r&   r'   zPTestCheckUnusedImports.test_finding_category_is_unused_import.<locals>.<genexpr>   s     F1Z=O3Fr(   r)   r   r+   Nr   r   s         r&   &test_finding_category_is_unused_importz=TestCheckUnusedImports.test_finding_category_is_unused_import   sx    &33IwGFXFFsFFFFFFFFFsFFFsFFFFFFFFFFFFFFrD   c                 T   d}t         j                  d|      }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!   import as 별칭 사용 케이스z-import numpy as np

arr = np.array([1, 2, 3])r   r   rF   rX   r   r   r   r   r   Nr   rP   s           r&   test_import_as_alias_usedz0TestCheckUnusedImports.test_import_as_alias_used   s    C33IwG8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   c                 T   d}t         j                  d|      }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   import as 별칭 미사용zimport numpy as np

x = 1r   r   r   r   r   r   r   r   r   Nr   rP   s           r&   test_import_as_alias_unusedz2TestCheckUnusedImports.test_import_as_alias_unused   s    /33IwG8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   c                 T   d}t         j                  d|      }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    from X import Y 사용 케이스z1from os.path import join

result = join('a', 'b')r   r   rF   rX   r   r   r   r   r   Nr   rP   s           r&   test_from_import_usedz,TestCheckUnusedImports.test_from_import_used   s    G33IwG8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   c                    d}t         j                  d|      }d |D        }t        |      }|sd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)u#   from X import Y 미사용 케이스zfrom os.path import join

x = 1r   c              3   *   K   | ]  }d |d   v   yw)joinr   Nr"   r#   s     r&   r'   zATestCheckUnusedImports.test_from_import_unused.<locals>.<genexpr>  s     <a6Qy\)<r   r)   r*   r+   Nr   r   s         r&   test_from_import_unusedz.TestCheckUnusedImports.test_from_import_unused  sx    533IwG<8<<s<<<<<<<<<s<<<s<<<<<<<<<<<<<<rD   c                 |   t         j                  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  }dd|iz  }t        t        j                  |            d	x}}y	rg   
r   r   r1   r2   r3   r4   r5   r6   r7   r8   rj   s         r&   rm   z=TestCheckUnusedImports.test_empty_content_returns_no_findings  sl    33IrBx2~x2xx2rD   c                 :   d}t         j                  d|      }|d   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)r^   r   r   r   r_   r   rF   rH   rI   rJ   r   N)r   r   r1   r2   r6   r7   r8   rd   s           r&   ra   z3TestCheckUnusedImports.test_finding_has_line_number  so    &33IwG{6"'a'"a''''"a'''"'''a'''''''rD   c                    d}t         j                  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  }dd|iz  }t        t        j                  |            d	x}}y	)
)   Python 파일 아닌 경우 탐지 없음zimport somethingtest.jsrF   ri   r   r	   r   r   Nr   r   s          r&   "test_non_python_file_returns_emptyz9TestCheckUnusedImports.test_non_python_file_returns_empty  sq    $33IwGx2~x2xx2rD   N)rs   rt   ru   rv   r   r   r   r   r   r   r   r   r   rm   ra   r   r"   rD   r&   r   r      sC    +;""AG"""=
(rD   r   c                   h    e Zd ZdZddededef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)TestCheckFunctionLengthu(   과도한 함수 길이 탐지 테스트linesnamereturnc                 \    dj                  d t        |dz
        D              }d| d| dS )u"   지정된 줄 수의 함수 생성
c              3   ,   K   | ]  }d | d|   ywz    x_z = Nr"   r$   is     r&   r'   z>TestCheckFunctionLength._make_long_function.<locals>.<genexpr>$  s     F6!Cs+Fr(   r   zdef z():

    return x_0
)r   range)r9   r   r   bodys       r&   _make_long_functionz+TestCheckFunctionLength._make_long_function"  s5    yyFU5195EFFdV5&899rD   c                 x   | j                  d      }t        j                  d|      }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}}|d   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)u   50줄 초과 함수 탐지7   r   r   r   r   r   r   r   r   r   Nr   r.   long_functionrF   rH   rI   rJ   r   r   r   check_function_lengthr   r1   r2   r3   r4   r5   r6   r7   r8   rL   s              r&   test_detects_long_functionz2TestCheckFunctionLength.test_detects_long_function'  s   **2.44YH8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!{:&9/9&/9999&/999&999/9999999rD   c                 r   | j                  d      }t        j                  d|      }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%   50줄 이하 함수는 탐지 안 함   r   r   rF   rX   r   r   r   r   r   Nr   rP   s           r&   "test_no_finding_for_short_functionz:TestCheckFunctionLength.test_no_finding_for_short_function.  s    **2.44YH8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   c                 r   | j                  d      }t        j                  d|      }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   정확히 50줄은 탐지 안 함 (>50 조건).

        _make_long_function(n)은 AST에서 n+1줄 함수를 생성하므로
        정확히 50줄 함수를 만들려면 n=49를 사용한다.
        1   r   r   rF   rX   r   r   r   r   r   Nr   rP   s           r&   test_exact_50_lines_not_flaggedz7TestCheckFunctionLength.test_exact_50_lines_not_flagged4       **2.44YH8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   c                 r   | j                  d      }t        j                  d|      }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   51줄 함수는 탐지됨.

        _make_long_function(n)은 AST에서 n+1줄 함수를 생성하므로
        51줄 함수를 만들려면 n=50을 사용한다.
        2   r   r   rF   rX   r   r   r   r   r   Nr   rP   s           r&   test_51_lines_flaggedz-TestCheckFunctionLength.test_51_lines_flagged>  r   rD   c                 X   | j                  d      }t        j                  d|      }|d   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)u,   과도한 함수 길이 심각도는 warning<   r   r   r    r   rF   rH   rI   rJ   r   Nr   r   r   r1   r2   r6   r7   r8   rd   s           r&   r   z8TestCheckFunctionLength.test_finding_severity_is_warningH  sz    **2.44YH{:&3)3&)3333&)333&333)3333333rD   c                 v   | j                  d      }t        j                  d|d      }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!   사용자 정의 max_lines 사용   r      )	max_linesr   r   r   r   r   r   r   r   Nr   rP   s           r&   test_custom_max_linesz-TestCheckFunctionLength.test_custom_max_linesN  s    **2.44YSU4V8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   c                    | j                  dd      }| j                  dd      }|dz   |z   }t        j                  d|      }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   여러 개의 긴 함수 탐지r   func_ar   func_br   r   rq   rF   rX   r   r   r   r   r   Nr   )
r9   func1func2r:   r   r;   r<   r=   r>   r?   s
             r&   test_multiple_long_functionsz4TestCheckFunctionLength.test_multiple_long_functionsT  s    ((X6((X6$,&44YH8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   c                 X   | j                  dd      }t        j                  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}}y)u$   발견 메시지에 함수명 포함r   my_long_functionr   r   r   r   r   rI   rJ   r   Nr   rd   s           r&   #test_finding_contains_function_namez;TestCheckFunctionLength.test_finding_contains_function_name\  s    **2/AB44YH!;Xa[%;;!%;;;;;!%;;;;!;;;%;;;;;;;;rD   c                 ^   d| j                  d      z   }t        j                  d|      }|d   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)u)   발견 항목에 시작 줄 번호 포함z	# header
r   r   r   r_   rq   rF   rH   rI   rJ   r   Nr   rd   s           r&   test_finding_has_start_linez3TestCheckFunctionLength.test_finding_has_start_lineb  s    !9!9"!==44YH{6"'a'"a''''"a'''"'''a'''''''rD   c                 |   t         j                  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  }dd|iz  }t        t        j                  |            d	x}}y	rg   )
r   r   r1   r2   r3   r4   r5   r6   r7   r8   rj   s         r&   rm   z>TestCheckFunctionLength.test_empty_content_returns_no_findingsh  sl    44YCx2~x2xx2rD   c                    | j                  d      }t        j                  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  }dd|iz  }t        t        j                  |            d	x}}y	)
r   r   r   rF   ri   r   r	   r   r   N)r   r   r   r1   r2   r3   r4   r5   r6   r7   r8   r   s          r&   r   z:TestCheckFunctionLength.test_non_python_file_returns_emptym  s|    **2.44YHx2~x2xx2rD   N)	long_func)rs   rt   ru   rv   intstrr   r   r   r   r   r   r   r   r   r   rm   r   r"   rD   r&   r   r     sV    2: :C :# :
:"""4""<(
rD   r   c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)TestReviewFileu   파일 종합 리뷰 테스트c                    t         j                  t        |dz              }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/   존재하지 않는 파일은 빈 결과 반환znonexistent.pyrF   ri   r   r	   r   r   N)r   review_filer   r1   r2   r3   r4   r5   r6   r7   r8   )r9   tmp_pathr   r;   r@   rk   rl   s          r&   test_review_nonexistent_filez+TestReviewFile.test_review_nonexistent_file|  su    **3x:J/J+KLx2~x2xx2rD   c                    |dz  }|j                  dd       t        j                  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   빈 파일은 탐지 없음zempty.pyrh   utf-8encodingrF   ri   r   r	   r   r   N)
write_textr   r   r   r1   r2   r3   r4   r5   r6   r7   r8   )r9   r   r%   r   r;   r@   rk   rl   s           r&   test_review_empty_filez%TestReviewFile.test_review_empty_file  s    z!	R'***3q62x2~x2xx2rD   c                 6   d}|dz  }|j                  |d       t        j                  t        |            }|D ch c]  }|d   	 }}d}||v }|st	        j
                  d|fd||f      t	        j                  |      d	t        j                         v st	        j                  |      rt	        j                  |      nd	d
z  }	dd|	iz  }
t        t	        j                  |
            dx}}d}||v }|st	        j
                  d|fd||f      t	        j                  |      d	t        j                         v st	        j                  |      rt	        j                  |      nd	d
z  }	dd|	iz  }
t        t	        j                  |
            dx}}yc c}w )u"   모든 이슈 포함 파일 리뷰zGimport os
import sys
# TODO: fix this
api_key = "AKIAIOSFODNN7EXAMPLE"
z
problem.pyr  r  r.   r/   r   z%(py1)s in %(py3)s
categoriesr   r   r   r   Nr{   r  r   r   r   r1   r2   r6   r3   r4   r5   r7   r8   )r9   r   r:   r%   r   fir
  rM   r;   rk   rl   s              r&    test_review_file_with_all_issuesz/TestReviewFile.test_review_file_with_all_issues  s   h|#	Ww/**3q62/78bn8
8!/!Z////!Z///!//////Z///Z///////#v####v###v################ 9s   Fc                    |dz  }|j                  t        t        d                   t        j	                  t        |            }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&   바이너리 파일은 graceful 처리z
binary.bin   5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}
isinstancer   listr
   r   r   r,   N)write_bytesbytesr   r   r   r   r  r  r3   r4   r1   r5   r6   r7   r8   )r9   r   r%   r   rA   rB   s         r&    test_review_binary_file_gracefulz/TestReviewFile.test_review_binary_file_graceful  s    |#	eE#J'(**3q62(D))))))))z)))z))))))()))())))))D)))D))))))))))rD   c                    |dz  }|j                  dd       t        j                  t        |            }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&   review_file은 항상 리스트 반환r   x = 1
r  r  r  r  resultr  r  Nr  r   r   r   r  r  r3   r4   r1   r5   r6   r7   r8   )r9   r   r%   r  rA   rB   s         r&   test_review_file_returns_listz,TestReviewFile.test_review_file_returns_list  s    y 	Y1((Q0&$''''''''z'''z''''''&'''&''''''$'''$''''''''''rD   N)	rs   rt   ru   rv   r  r  r  r  r  r"   rD   r&   r   r   y  s    (
$*(rD   r   c                   4    e Zd ZdZd Zd Zd Zd Zd Zd Z	y)	TestReviewAllu%   전체 리뷰 결과 집계 테스트c                 J   |dz  }|j                  dd       t        j                  t        |      g      }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}}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}}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)u   review_all 결과 구조 검증r   r  r  r  files_analyzedr   r	  r  r  r   r   Nr   summaryr!   r   rI   rJ   r   r   r|   )r  r   
review_allr   r1   r2   r6   r3   r4   r5   r7   r8   )r9   r   r%   r  rM   r;   rk   rl   rA   rB   r>   s              r&   test_review_all_structurez'TestReviewAll.test_review_all_structure  s6   y 	Y1''Q1)6))))6)))))))))6)))6)))))))#zV####zV###z######V###V#######"yF""""yF"""y""""""F"""F""""""".VI..z.....z....z...........-F9--y-----y----y-----------*	**v*****v****v***********rD   c                    g }t        d      D ]8  }|d| dz  }|j                  dd       |j                  t        |             : t        j                  |      }|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)u   분석된 파일 수 정확성r`   test_.pyr  r  r  r   rF   rH   rI   rJ   r   N)r   r  appendr   r   r"  r1   r2   r6   r7   r8   )r9   r   filesr   r%   r  rM   rA   r;   rB   r>   s              r&   $test_review_all_files_analyzed_countz2TestReviewAll.test_review_all_files_analyzed_count  s    q 	!AU1#S>)ALLWL5LLQ 	! ''.&',1,'1,,,,'1,,,',,,1,,,,,,,rD   c                    |dz  }d}|j                  |d       t        j                  t        |      g      }|d   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}}|d   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)u'   심각도별 요약 카운트 정확성r   7api_key = "AKIAIOSFODNN7EXAMPLE"
import os
# TODO: fix
r  r  r!  r!   r   r   )z%(py1)s >= %(py4)srI   rJ   r   Nr|   )	r  r   r"  r   r1   r2   r6   r7   r8   )
r9   r   r%   r:   r  rM   rA   r;   rB   r>   s
             r&   test_review_all_summary_countsz,TestReviewAll.test_review_all_summary_counts  s    y T	Ww/''Q1i ,11,1111,111,1111111111i (-A-(A----(A---(---A-------rD   c                 @   t         j                  g       }|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}}|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   }d}||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   }d}||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   }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)u   빈 파일 목록 처리r   r   rF   rH   rI   rJ   r   Nr   r!  r!   r   r|   )r   r"  r1   r2   r6   r7   r8   )r9   r  rM   rA   r;   rB   r>   s          r&   test_review_all_empty_listz(TestReviewAll.test_review_all_empty_list  s   ''+&',1,'1,,,,'1,,,',,,1,,,,,,,j!'R'!R''''!R'''!'''R'''''''i ,11,1111,111,1111111111i +0q0+q0000+q000+000q0000000i (-A-(A----(A---(---A-------rD   c                 d   |dz  }|j                  dd       t        j                  t        |      g      }|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"   findings가 리스트인지 확인r   z# TODO: test
r  r  r   5assert %(py5)s
{%(py5)s = %(py0)s(%(py2)s, %(py3)s)
}r  r  r
   r   r   r   N)r  r   r"  r   r  r  r3   r4   r1   r5   r6   r7   r8   )r9   r   r%   r  r@   r=   rl   s          r&   test_review_all_findings_listz+TestReviewAll.test_review_all_findings_list  s    y 	%8''Q1 ,3z,d33333333z333z333,333333d333d3333333333rD   c                    |dz  }|j                  dd       t        j                  t        |      g      }t	        j
                  |      }t	        j                  |      }|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)u,   결과가 JSON 직렬화 가능한지 확인r   z!api_key = "AKIAIOSFODNN7EXAMPLE"
r  r  r   r   rF   rH   rI   rJ   r   N)r  r   r"  r   jsondumpsloadsr1   r2   r6   r7   r8   )r9   r   r%   r  json_strparsedrM   rA   r;   rB   r>   s              r&   !test_review_all_json_serializablez/TestReviewAll.test_review_all_json_serializable  s    y 	9GL''Q1::f%H%&',1,'1,,,,'1,,,',,,1,,,,,,,rD   N)
rs   rt   ru   rv   r#  r)  r,  r.  r2  r9  r"   rD   r&   r  r    s#    /
+-..4-rD   r  c                   4    e Zd ZdZd Zd Zd Zd Zd Zd Z	y)	TestGetChangedFilesu"   get_changed_files 함수 테스트c                    d}t        d      5 }t        |d      |_        t        j	                  d      }ddd       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  }dd|iz  }t        t        j                  |            dx}}y# 1 sw Y   xY w)u   파일 목록 반환 확인zscripts/foo.py
scripts/bar.py
subprocess.runr   stdout
returncodeHEAD~1..HEADNzscripts/foo.pyzscripts/bar.pyrF   ri   r(  r	   r   r   r   r   return_valuer   get_changed_filesr1   r2   r3   r4   r5   r6   r7   r8   )r9   mock_outputmock_runr(  r;   r@   rk   rl   s           r&   test_returns_list_of_filesz.TestGetChangedFiles.test_returns_list_of_files  s    8#$ 	B$-"%H!  11.AE	B *+;<<u<<<<<u<<<<<<<u<<<u<<<<<<<<<<<	B 	Bs   (C''C0c                    t        d      5 }t        dd      |_        t        j	                  d      }d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  }dd|iz  }t        t        j                  |            dx}}y# 1 sw Y   xY w)u,   변경 파일 없으면 빈 리스트 반환r=  rh   r   r>  rA  NrF   ri   r(  r	   r   r   rB  r9   rF  r(  r;   r@   rk   rl   s          r&   "test_empty_diff_returns_empty_listz6TestGetChangedFiles.test_empty_diff_returns_empty_list  s    #$ 	B$-%H!  11.AE	B u{uuu	B 	Bs   (C##C,c                    t        d      5 }t        j                  ddd      |_        t        j                  d      }d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  }dd|iz  }t        t        j                  |            dx}}y# 1 sw Y   xY w)u.   git 실패 시 빈 리스트 반환 (graceful)r=  r   gitznot a git repository)stderrrA  NrF   ri   r(  r	   r   r   )r   
subprocessCalledProcessErrorside_effectr   rD  r1   r2   r3   r4   r5   r6   r7   r8   rI  s          r&   #test_git_failure_returns_empty_listz7TestGetChangedFiles.test_git_failure_returns_empty_list  s    #$ 	B#-#@#@ERh#iH 11.AE	B u{uuu	B 	Bs   3C..C7c                    t        d      5 }t        d      |_        t        j	                  d      }d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  }d	d
|iz  }t        t        j                  |            dx}}y# 1 sw Y   xY w)u*   git 명령 없을 때 빈 리스트 반환r=  zgit not foundrA  NrF   ri   r(  r	   r   r   )r   FileNotFoundErrorrP  r   rD  r1   r2   r3   r4   r5   r6   r7   r8   rI  s          r&   %test_git_not_found_returns_empty_listz9TestGetChangedFiles.test_git_not_found_returns_empty_list  s    #$ 	B#4_#EH 11.AE	B u{uuu	B 	Bs   &C!!C*c                    d}t        d      5 }t        |d      |_        t        j	                  d      }ddd       d D        }t        |      }|sd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# 1 sw Y   xY w)u&   Python 파일만 필터링 (선택적)z(scripts/foo.py
README.md
scripts/bar.py
r=  r   r>  rA  Nc              3   >   K   | ]  }|j                  d         yw)r&  N)endswithr#   s     r&   r'   zDTestGetChangedFiles.test_filters_non_python_files.<locals>.<genexpr>  s     41::e$4s   r)   r*   r+   )r   r   rC  r   rD  r*   r3   r4   r1   r5   r6   r7   r8   )r9   rE  rF  r(  r@   rA   rB   s          r&   test_filters_non_python_filesz1TestGetChangedFiles.test_filters_non_python_files  s    C#$ 	B$-"%H!  11.AE	B 5e44s444444444s444s44444444444444	B 	Bs   (C..C7c                    t        d      5 }t        dd      |_        t        j	                         }d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# 1 sw Y   +xY w)u   기본 diff range (HEAD) 사용r=  zscripts/foo.py
r   r>  Nr  r  r(  r  r  )r   r   rC  r   rD  r  r  r3   r4   r1   r5   r6   r7   r8   )r9   rF  r(  rA   rB   s        r&   test_default_diff_rangez+TestGetChangedFiles.test_default_diff_range  s    #$ 	4$-)%H!  113E	4 %&&&&&&&&z&&&z&&&&&&%&&&%&&&&&&&&&&&&&&&&&&&	4 	4s   'EE&N)
rs   rt   ru   rv   rG  rJ  rQ  rT  rX  rZ  r"   rD   r&   r;  r;    s#    ,	=
5'rD   r;  c                   4    e Zd ZdZd Zd Zd Zd Zd Zd Z	y)	TestEdgeCasesu!   에지 케이스 처리 테스트c                    dgdz  }|dz  }|j                  dj                  |      d       t        j                  t	        |            }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   대용량 파일 처리r  i'  zlarge.pyrh   r  r  r  r  r   r  r  N)r  r   r   r   r   r  r  r3   r4   r1   r5   r6   r7   r8   )r9   r   r   r%   r   rA   rB   s          r&   test_very_large_filez"TestEdgeCases.test_very_large_file,  s    e#z!	RWWU^g6**3q62(D))))))))z)))z))))))()))())))))D)))D))))))))))rD   c                    d}|dz  }|j                  |d       t        j                  t        |            }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   주석만 있는 파일z&# This is a comment
# Another comment
zcomments.pyr  r  r  r  r   r  r  Nr  r9   r   r:   r%   r   rA   rB   s          r&   test_file_with_only_commentsz*TestEdgeCases.test_file_with_only_comments4  s    <}$	Ww/**3q62(D))))))))z)))z))))))()))())))))D)))D))))))))))rD   c                    d}|dz  }|j                  |d       t        j                  t        |            }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   유니코드 내용 처리u:   # 한글 주석
# TODO: 한글 TODO
x = '안녕하세요'
z
unicode.pyr  r  r  r  r   r  r  Nr  r`  s          r&   test_unicode_contentz"TestEdgeCases.test_unicode_content<  s    Q|#	Ww/**3q62(D))))))))z)))z))))))()))())))))D)))D))))))))))rD   c                    d}|dz  }|j                  |j                  d             t        j                  t	        |            }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   Windows 줄 끝 처리zimport os
# TODO: fix
x = 1
z
windows.pyr  r  r  r   r  r  N)r  encoder   r   r   r  r  r3   r4   r1   r5   r6   r7   r8   r`  s          r&   test_windows_line_endingsz'TestEdgeCases.test_windows_line_endingsD  s    9|#	gnnW-.**3q62(D))))))))z)))z))))))()))())))))D)))D))))))))))rD   c                 	   d}|dz  }|j                  |d       t        j                  t        |            }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}}|D ]<  }
d}||
v }|st        j                  d|fd||
f      t        j                  |      dt        j                         v st        j                  |
      rt        j                  |
      nddz  }dd|iz  }t        t        j                  |            dx}}d}||
v }|st        j                  d|fd||
f      t        j                  |      dt        j                         v st        j                  |
      rt        j                  |
      nddz  }dd|iz  }t        t        j                  |            dx}}d}||
v }|st        j                  d|fd||
f      t        j                  |      dt        j                         v st        j                  |
      rt        j                  |
      nddz  }dd|iz  }t        t        j                  |            dx}}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)u   Finding 구조 완전성 검증z# TODO: test finding structurer   r  r  r   r   r   r   r   r   r   r   Nrc   r   r	  findingr  r   r   r_   r    r.   r   )r  r   r   r   r   r1   r2   r3   r4   r5   r6   r7   r8   )r9   r   r:   r%   r   r;   r<   r=   r>   r?   rh  rM   rk   rl   s                 r&   test_finding_structure_completez-TestEdgeCases.test_finding_structure_completeL  s   2y 	Ww/**3q628}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!! 	(G$6W$$$$6W$$$6$$$$$$W$$$W$$$$$$$$6W$$$$6W$$$6$$$$$$W$$$W$$$$$$$(:((((:(((:(((((((((((((((((:((((:(((:(((((((((((((((('9''''9'''9''''''''''''''''	(rD   c                    d}|dz  }|j                  |d       t        j                  t        |            }h 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}} y)u2   심각도 값이 유효한 범위 내인지 확인r+  r   r  r  >   r|   r   r!   r    r   r	  valid_severitiesr  r   r   Nr  )r9   r   r:   r%   r   rk  rh  rM   r;   rk   rl   s              r&   test_severity_values_validz(TestEdgeCases.test_severity_values_validZ  s    Ty 	Ww/**3q62: 	;G:&:&*:::::&*::::&::::::*::::*::::::::	;rD   N)
rs   rt   ru   rv   r^  ra  rc  rf  ri  rl  r"   rD   r&   r\  r\  )  s#    +****(;rD   r\  c                   (    e Zd ZdZd Zd Zd Zd Zy)TestFindingStructureu"   Finding 데이터 구조 테스트c                 l   d}t         j                  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}|r|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y)u0   check_hardcoded_secrets가 dict 리스트 반환zapi_key = "secret123"r   r  r  r   r  r  Nr   r0  dictr1  )r   r0   r  r  r3   r4   r1   r5   r6   r7   r8   rp  r9   r:   r   rA   rB   r@   r=   rl   s           r&   .test_check_hardcoded_secrets_returns_dict_listzCTestFindingStructure.test_check_hardcoded_secrets_returns_dict_listm  s&   )66y'J(D))))))))z)))z))))))()))())))))D)))D))))))))))&qk0:k400000000:000:000k000000400040000000000 rD   c                 l   d}t         j                  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}|r|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y)u$   check_todos가 dict 리스트 반환z# TODO: testr   r  r  r   r  r  Nr   r0  rp  r1  )r   r~   r  r  r3   r4   r1   r5   r6   r7   r8   rp  rq  s           r&   "test_check_todos_returns_dict_listz7TestFindingStructure.test_check_todos_returns_dict_listu  s&    **9g>(D))))))))z)))z))))))()))())))))D)))D))))))))))&qk0:k400000000:000:000k000000400040000000000 rD   c                 l   d}t         j                  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}|r|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y)u-   check_unused_imports가 dict 리스트 반환zimport os
x = 1r   r  r  r   r  r  Nr   r0  rp  r1  )r   r   r  r  r3   r4   r1   r5   r6   r7   r8   rp  rq  s           r&   +test_check_unused_imports_returns_dict_listz@TestFindingStructure.test_check_unused_imports_returns_dict_list}  s&   $33IwG(D))))))))z)))z))))))()))())))))D)))D))))))))))&qk0:k400000000:000:000k000000400040000000000 rD   c                    dj                  d t        d      D              }d| d}t        j                  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}|r|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y)u.   check_function_length가 dict 리스트 반환r   c              3   ,   K   | ]  }d | d|   ywr   r"   r   s     r&   r'   zTTestFindingStructure.test_check_function_length_returns_dict_list.<locals>.<genexpr>  s     ?6!Cs+?r(   4   zdef func():
r   r   r  r  r   r  r  Nr   r0  rp  r1  )r   r   r   r   r  r  r3   r4   r1   r5   r6   r7   r8   rp  )	r9   r   r:   r   rA   rB   r@   r=   rl   s	            r&   ,test_check_function_length_returns_dict_listzATestFindingStructure.test_check_function_length_returns_dict_list  sH   yy?U2Y??!$'9:44YH(D))))))))z)))z))))))()))())))))D)))D))))))))))&qk0:k400000000:000:000k000000400040000000000 rD   N)rs   rt   ru   rv   rr  rt  rv  rz  r"   rD   r&   rn  rn  j  s    ,1111rD   rn  c                   (    e Zd ZdZd Zd Zd Zd Zy)TestAdditionalSecretPatternsu(   추가 시크릿 패턴 탐지 테스트c                 T   d}t         j                  d|      }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+   DB URL에 자격증명 포함 패턴 탐지z3DATABASE_URL = "postgresql://user:password@host/db"r   r   r   r   r   r   r   r   r   NrK   rP   s           r&   *test_detects_database_url_with_credentialszGTestAdditionalSecretPatterns.test_detects_database_url_with_credentials  s    G66y'J8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   c                 t   d}t         j                  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<   localhost URL은 탐지 안 함 (자격증명 없는 경우)z,DATABASE_URL = "postgresql://localhost/mydb"r   r  r  r   r  r  Nr   r0   r  r  r3   r4   r1   r5   r6   r7   r8   r9   r:   r   rA   rB   s        r&    test_no_false_positive_localhostz=TestAdditionalSecretPatterns.test_no_false_positive_localhost  s    @66y'J (D))))))))z)))z))))))()))())))))D)))D))))))))))rD   c                 T   d}t         j                  d|      }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   Bearer 토큰 패턴 탐지zJheaders = {"Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"}r   r   r   r   r   r   r   r   r   NrK   rP   s           r&   test_detects_bearer_tokenz6TestAdditionalSecretPatterns.test_detects_bearer_token  s    ^66y'J8}!!}!!!!}!!!!!!s!!!s!!!!!!8!!!8!!!}!!!!!!!!!!rD   c                 t   d}t         j                  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$   플레이스홀더는 탐지 안 함zapi_key = "<YOUR_API_KEY>"r   r  r  r   r  r  Nr  r  s        r&   "test_no_false_positive_placeholderz?TestAdditionalSecretPatterns.test_no_false_positive_placeholder  s    .66y'J(D))))))))z)))z))))))()))())))))D)))D))))))))))rD   N)rs   rt   ru   rv   r~  r  r  r  r"   rD   r&   r|  r|    s    2"*"*rD   r|  )7rv   builtinsr3   _pytest.assertion.rewrite	assertionrewriter1   importlib.util	importlibr4  rN  syspathlibr   unittest.mockr   r   pytest__file__parent_SCRIPTS_DIRpathinsertr   _MODULE_PATHutilspec_from_file_locationr   r;   r@   r2   r4   r5   r6   rk   rl   r7   r8   module_from_specr   loaderr=   rA   @py_format8exec_moduler   rx   r   r   r   r  r;  r\  rn  r|  r"   rD   r&   <module>r     s  "      
  *  H~$$++ 3|$ % ..~~--m\J t4   t4     t   t   4      nn--d3{{ $ {$   {$     t   t   {  $          $L" L"hE EZI IbR Rt%( %(Z9- 9-B:' :'D9; 9;B"1 "1T* *rD   