
    (<im                     b   d 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j&                  j)                  de      ZeJ ej&                  j-                  e      Zej0                  J ej0                  j3                  e        G d d      Z G d	 d
      Z G d d      Z G d d      Z G d d      Z G d d      Z G d d      Z  G d d      Z! G d d      Z" G d d      Z#y)u  
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_reviewc                   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\  sJ t        d |D              sJ t        d |D              sJ y)u   AWS Access Key ID 탐지z*AWS_ACCESS_KEY_ID = "AKIAIOSFODNN7EXAMPLE"test.py   c              3   ,   K   | ]  }|d    dk(    yw)severitycriticalN .0fs     O/home/jay/workspace/.worktrees/task-2057-dev2/scripts/tests/test_code_review.py	<genexpr>zHTestCheckHardcodedSecrets.test_detects_aws_access_key.<locals>.<genexpr>5   s     A11Z=J.A   c              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   N)r   check_hardcoded_secretslenanyselfcontentfindingss      r   test_detects_aws_access_keyz5TestCheckHardcodedSecrets.test_detects_aws_access_key0   sS    >66y'J8}!!!AAAAAIIIII    c                 n    d}t         j                  d|      }t        |      dk\  sJ |d   d   dk(  sJ y)u   api_key = '...' 패턴 탐지zapi_key = 'sk-1234567890abcdef'r
   r   r   r   r   Nr   r   r   r   s      r   test_detects_api_key_assignmentz9TestCheckHardcodedSecrets.test_detects_api_key_assignment8   sB    366y'J8}!!!{:&*444r!   c                 T    d}t         j                  d|      }t        |      dk\  sJ y)u   password = '...' 패턴 탐지z&password = "super_secret_password_123"r
   r   Nr#   r   s      r    test_detects_password_assignmentz:TestCheckHardcodedSecrets.test_detects_password_assignment?   s+    :66y'J8}!!!r!   c                 T    d}t         j                  d|      }t        |      dk\  sJ y)u   token = '...' 패턴 탐지z"token = "ghp_1234567890abcdefghij"r
   r   Nr#   r   s      r   test_detects_token_assignmentz7TestCheckHardcodedSecrets.test_detects_token_assignmentE   +    666y'J8}!!!r!   c                 T    d}t         j                  d|      }t        |      dk\  sJ y)u    secret_key = '...' 패턴 탐지z"secret_key = "my-super-secret-key"r
   r   Nr#   r   s      r   "test_detects_secret_key_assignmentz<TestCheckHardcodedSecrets.test_detects_secret_key_assignmentK   r)   r!   c                 T    d}t         j                  d|      }t        |      dk(  sJ y)u%   환경변수 참조는 탐지 안 함z#api_key = os.environ.get("API_KEY")r
   r   Nr#   r   s      r   test_no_false_positive_env_varz8TestCheckHardcodedSecrets.test_no_false_positive_env_varQ   +    766y'J8}!!!r!   c                 T    d}t         j                  d|      }t        |      dk(  sJ y)u&   빈 문자열 할당은 탐지 안 함zpassword = ""r
   r   Nr#   r   s      r   #test_no_false_positive_empty_stringz=TestCheckHardcodedSecrets.test_no_false_positive_empty_stringW   s+    !66y'J8}!!!r!   c                 n    d}t         j                  d|      }t        |      dk\  sJ |d   d   dk(  sJ y)"   발견 항목에 줄 번호 포함z'# line 1
# line 2
api_key = 'secret123'r
   r   r   line   Nr#   r   s      r   test_finding_has_line_numberz6TestCheckHardcodedSecrets.test_finding_has_line_number]   sB    =66y'J8}!!!{6"a'''r!   c                 N    d}t         j                  d|      }|d   d   dk(  sJ y)u%   발견 항목에 파일 경로 포함zpassword = "secret"zscripts/test.pyr   fileNr   r   r   s      r   test_finding_has_file_pathz4TestCheckHardcodedSecrets.test_finding_has_file_pathd   s2    '667H'R{6"&7777r!   c                 >    t         j                  dd      }|g k(  sJ yu   빈 파일에서 탐지 없음r
    Nr8   r   r   s     r   &test_empty_content_returns_no_findingsz@TestCheckHardcodedSecrets.test_empty_content_returns_no_findingsj   s     66y"E2~~r!   c                 T    d}t         j                  d|      }t        |      dk\  sJ y)u)   -----BEGIN PRIVATE KEY----- 헤더 탐지z#key = '-----BEGIN PRIVATE KEY-----'r
   r   Nr#   r   s      r   test_detects_private_key_headerz9TestCheckHardcodedSecrets.test_detects_private_key_headero   r.   r!   c                 T    d}t         j                  d|      }t        |      dk\  sJ y)u,   여러 줄에 걸쳐 있는 시크릿 탐지zHx = 1
y = 2
api_key = "AKIAIOSFODNN7EXAMPLE"
z = 3
password = "hunter2"
r
      Nr#   r   s      r   test_multiline_detectionz2TestCheckHardcodedSecrets.test_multiline_detectionu   s+    m66y'J8}!!!r!   N)__name__
__module____qualname____doc__r    r$   r&   r(   r+   r-   r0   r5   r9   r>   r@   rC   r   r!   r   r   r   -   sB    4J5"""""(8
""r!   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(  sJ |d   d   dk(  sJ |d   d   dk(  sJ y	)
u   # TODO: 패턴 탐지# TODO: fix this laterr
   r   r   r   todor   infoNr   check_todosr   r   s      r   test_detects_todo_commentz(TestCheckTodos.test_detects_todo_comment   sY    ***9g>8}!!!{:&&000{:&&000r!   c                 n    d}t         j                  d|      }t        |      dk(  sJ |d   d   dk(  sJ y)u   # FIXME: 패턴 탐지z# FIXME: broken logic herer
   r   r   r   rL   NrN   r   s      r   test_detects_fixme_commentz)TestCheckTodos.test_detects_fixme_comment   sB    .**9g>8}!!!{:&&000r!   c                 T    d}t         j                  d|      }t        |      dk(  sJ y)u   인라인 TODO 탐지zx = 1  # TODO: remove thisr
   r   NrN   r   s      r   test_detects_todo_inlinez'TestCheckTodos.test_detects_todo_inline   s+    .**9g>8}!!!r!   c                 T    d}t         j                  d|      }t        |      dk\  sJ y)u   # HACK: 패턴 탐지z# HACK: temporary workaroundr
   r   NrN   r   s      r   test_detects_hack_commentz(TestCheckTodos.test_detects_hack_comment   s+    0**9g>8}!!!r!   c                 T    d}t         j                  d|      }t        |      dk\  sJ y)u   # XXX: 패턴 탐지z# XXX: this needs attentionr
   r   NrN   r   s      r   test_detects_xxx_commentz'TestCheckTodos.test_detects_xxx_comment   s+    /**9g>8}!!!r!   c                 T    d}t         j                  d|      }t        |      dk\  sJ y)u   대소문자 무관 탐지z# todo: lowercase todor
   r   NrN   r   s      r   test_case_insensitive_todoz)TestCheckTodos.test_case_insensitive_todo   s+    ***9g>8}!!!r!   c                 L    d}t         j                  d|      }d|d   d   v sJ y)u&   발견 메시지에 TODO 내용 포함rK   r
   zfix this laterr   messageNr   rO   r   s      r   'test_finding_message_contains_todo_textz6TestCheckTodos.test_finding_message_contains_todo_text   s0    ***9g>8A;y#9999r!   c                 N    d}t         j                  d|      }|d   d   dk(  sJ y)u   줄 번호 정확성zx = 1
# TODO: check
y = 2r
   r   r3   rB   Nr]   r   s      r    test_finding_line_number_correctz/TestCheckTodos.test_finding_line_number_correct   s0    /**9g>{6"a'''r!   c                 T    d}t         j                  d|      }t        |      dk(  sJ y)u   여러 TODO 탐지z+# TODO: first
# FIXME: second
# TODO: thirdr
   r4   NrN   r   s      r   test_multiple_todos_detectedz+TestCheckTodos.test_multiple_todos_detected   s+    A**9g>8}!!!r!   c                 >    t         j                  dd      }|g k(  sJ yr;   r]   r=   s     r   r>   z5TestCheckTodos.test_empty_content_returns_no_findings   s     **9b92~~r!   c                 B    d}t         j                  d|      }|g k(  sJ y)u!   일반 코드에서 오탐 없음z8message = 'This is working correctly'
result = compute()r
   Nr]   r   s      r    test_no_false_positive_in_stringz/TestCheckTodos.test_no_false_positive_in_string   s%    M**9g>2~~r!   N)rD   rE   rF   rG   rP   rR   rT   rV   rX   rZ   r^   r`   rb   r>   re   r   r!   r   rI   rI      s<    ,11"""":("
r!   rI   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|      }t        d |D              sJ 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<':   Nr   check_unused_importsr   r   s      r   test_detects_unused_importz1TestCheckUnusedImports.test_detects_unused_import   s-    <33IwG:::::r!   c                 T    d}t         j                  d|      }t        |      dk(  sJ y)u"   사용된 import는 탐지 안 함z*import os

result = os.path.join('a', 'b')r
   r   Nr   rm   r   r   s      r   test_no_finding_for_used_importz6TestCheckUnusedImports.test_no_finding_for_used_import   s+    @33IwG8}!!!r!   c                 T    d}t         j                  d|      }t        |      dk\  sJ y)u   여러 미사용 import 탐지z'import os
import sys
import json

x = 1r
   rB   Nrp   r   s      r   $test_detects_multiple_unused_importsz;TestCheckUnusedImports.test_detects_multiple_unused_imports   s+    ?33IwG8}!!!r!   c                 \    d}t         j                  d|      }t        d |D              sJ 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   Nr   rm   allr   s      r    test_finding_severity_is_warningz7TestCheckUnusedImports.test_finding_severity_is_warning   s-    &33IwG@x@@@@r!   c                 \    d}t         j                  d|      }t        d |D              sJ y)u   발견 카테고리 확인ru   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   Nrx   r   s      r   &test_finding_category_is_unused_importz=TestCheckUnusedImports.test_finding_category_is_unused_import   s-    &33IwGFXFFFFr!   c                 T    d}t         j                  d|      }t        |      dk(  sJ y)u!   import as 별칭 사용 케이스z-import numpy as np

arr = np.array([1, 2, 3])r
   r   Nrp   r   s      r   test_import_as_alias_usedz0TestCheckUnusedImports.test_import_as_alias_used   s+    C33IwG8}!!!r!   c                 T    d}t         j                  d|      }t        |      dk\  sJ y)u   import as 별칭 미사용zimport numpy as np

x = 1r
   r   Nrp   r   s      r   test_import_as_alias_unusedz2TestCheckUnusedImports.test_import_as_alias_unused   s+    /33IwG8}!!!r!   c                 T    d}t         j                  d|      }t        |      dk(  sJ y)u    from X import Y 사용 케이스z1from os.path import join

result = join('a', 'b')r
   r   Nrp   r   s      r   test_from_import_usedz,TestCheckUnusedImports.test_from_import_used   s+    G33IwG8}!!!r!   c                 \    d}t         j                  d|      }t        d |D              sJ 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\)<rk   Nrl   r   s      r   test_from_import_unusedz.TestCheckUnusedImports.test_from_import_unused  s-    533IwG<8<<<<r!   c                 >    t         j                  dd      }|g k(  sJ yr;   r   rm   r=   s     r   r>   z=TestCheckUnusedImports.test_empty_content_returns_no_findings  s     33IrB2~~r!   c                 N    d}t         j                  d|      }|d   d   dk(  sJ y)r2   ru   r
   r   r3   r   Nr   r   s      r   r5   z3TestCheckUnusedImports.test_finding_has_line_number  s0    &33IwG{6"a'''r!   c                 B    d}t         j                  d|      }|g k(  sJ y))   Python 파일 아닌 경우 탐지 없음zimport somethingtest.jsNr   r   s      r   "test_non_python_file_returns_emptyz9TestCheckUnusedImports.test_non_python_file_returns_empty  s%    $33IwG2~~r!   N)rD   rE   rF   rG   rn   rq   rs   rz   r~   r   r   r   r   r>   r5   r   r   r!   r   rg   rg      sC    +;""AG"""=
(r!   rg   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)r   r   r   bodys       r   _make_long_functionz+TestCheckFunctionLength._make_long_function"  s5    yyFU5195EFFdV5&899r!   c                     | j                  d      }t        j                  d|      }t        |      dk\  sJ |d   d   dk(  sJ y)u   50줄 초과 함수 탐지7   r
   r   r   r   long_functionNr   r   check_function_lengthr   r   s      r   test_detects_long_functionz2TestCheckFunctionLength.test_detects_long_function'  sM    **2.44YH8}!!!{:&/999r!   c                 r    | j                  d      }t        j                  d|      }t        |      dk(  sJ y)u%   50줄 이하 함수는 탐지 안 함   r
   r   Nr   r   s      r   "test_no_finding_for_short_functionz:TestCheckFunctionLength.test_no_finding_for_short_function.  s6    **2.44YH8}!!!r!   c                 r    | j                  d      }t        j                  d|      }t        |      dk(  sJ y)u   정확히 50줄은 탐지 안 함 (>50 조건).

        _make_long_function(n)은 AST에서 n+1줄 함수를 생성하므로
        정확히 50줄 함수를 만들려면 n=49를 사용한다.
        1   r
   r   Nr   r   s      r   test_exact_50_lines_not_flaggedz7TestCheckFunctionLength.test_exact_50_lines_not_flagged4  8     **2.44YH8}!!!r!   c                 r    | j                  d      }t        j                  d|      }t        |      dk(  sJ y)u   51줄 함수는 탐지됨.

        _make_long_function(n)은 AST에서 n+1줄 함수를 생성하므로
        51줄 함수를 만들려면 n=50을 사용한다.
        2   r
   r   Nr   r   s      r   test_51_lines_flaggedz-TestCheckFunctionLength.test_51_lines_flagged>  r   r!   c                 l    | j                  d      }t        j                  d|      }|d   d   dk(  sJ y)u,   과도한 함수 길이 심각도는 warning<   r
   r   r   rw   Nr   r   r   r   s      r   rz   z8TestCheckFunctionLength.test_finding_severity_is_warningH  s;    **2.44YH{:&)333r!   c                 v    | j                  d      }t        j                  d|d      }t        |      dk\  sJ y)u!   사용자 정의 max_lines 사용   r
      )	max_linesr   Nr   r   s      r   test_custom_max_linesz-TestCheckFunctionLength.test_custom_max_linesN  s<    **2.44YSU4V8}!!!r!   c                     | j                  dd      }| j                  dd      }|dz   |z   }t        j                  d|      }t        |      dk(  sJ y)	u   여러 개의 긴 함수 탐지r   func_ar   func_br   r
   rB   Nr   )r   func1func2r   r   s        r   test_multiple_long_functionsz4TestCheckFunctionLength.test_multiple_long_functionsT  sX    ((X6((X6$,&44YH8}!!!r!   c                 l    | j                  dd      }t        j                  d|      }d|d   d   v sJ y)u$   발견 메시지에 함수명 포함r   my_long_functionr
   r   r\   Nr   r   s      r   #test_finding_contains_function_namez;TestCheckFunctionLength.test_finding_contains_function_name\  s>    **2/AB44YH!Xa[%;;;;r!   c                 r    d| j                  d      z   }t        j                  d|      }|d   d   dk(  sJ y)u)   발견 항목에 시작 줄 번호 포함z	# header
r   r
   r   r3   rB   Nr   r   s      r   test_finding_has_start_linez3TestCheckFunctionLength.test_finding_has_start_lineb  s@    !9!9"!==44YH{6"a'''r!   c                 >    t         j                  dd      }|g k(  sJ yr;   )r   r   r=   s     r   r>   z>TestCheckFunctionLength.test_empty_content_returns_no_findingsh  s     44YC2~~r!   c                 `    | j                  d      }t        j                  d|      }|g k(  sJ y)r   r   r   Nr   r   s      r   r   z:TestCheckFunctionLength.test_non_python_file_returns_emptym  s0    **2.44YH2~~r!   N)	long_func)rD   rE   rF   rG   intstrr   r   r   r   r   rz   r   r   r   r   r>   r   r   r!   r   r   r     sV    2: :C :# :
:"""4""<(
r!   r   c                   .    e Zd ZdZd Zd Zd Zd Zd Zy)TestReviewFileu   파일 종합 리뷰 테스트c                 T    t         j                  t        |dz              }|g k(  sJ y)u/   존재하지 않는 파일은 빈 결과 반환znonexistent.pyN)r   review_filer   )r   tmp_pathr   s      r   test_review_nonexistent_filez+TestReviewFile.test_review_nonexistent_file|  s)    **3x:J/J+KL2~~r!   c                 ~    |dz  }|j                  dd       t        j                  t        |            }|g k(  sJ y)u   빈 파일은 탐지 없음zempty.pyr<   utf-8encodingN
write_textr   r   r   r   r   r   r   s       r   test_review_empty_filez%TestReviewFile.test_review_empty_file  s<    z!	R'***3q622~~r!   c                     d}|dz  }|j                  |d       t        j                  t        |            }|D ch c]  }|d   	 }}d|v sJ d|v sJ yc c}w )	u"   모든 이슈 포함 파일 리뷰zGimport os
import sys
# TODO: fix this
api_key = "AKIAIOSFODNN7EXAMPLE"
z
problem.pyr   r   r   r   rL   Nr   )r   r   r   r   r   fi
categoriess          r    test_review_file_with_all_issuesz/TestReviewFile.test_review_file_with_all_issues  sm    h|#	Ww/**3q62/78bn8
8!Z///### 9s   Ac                     |dz  }|j                  t        t        d                   t        j	                  t        |            }t        |t              sJ y)u&   바이너리 파일은 graceful 처리z
binary.bin   N)write_bytesbytesr   r   r   r   
isinstancelistr   s       r    test_review_binary_file_gracefulz/TestReviewFile.test_review_binary_file_graceful  sE    |#	eE#J'(**3q62(D)))r!   c                     |dz  }|j                  dd       t        j                  t        |            }t	        |t
              sJ y)u&   review_file은 항상 리스트 반환r
   x = 1
r   r   Nr   r   r   r   r   r   r   r   r   results       r   test_review_file_returns_listz,TestReviewFile.test_review_file_returns_list  s@    y 	Y1((Q0&$'''r!   N)	rD   rE   rF   rG   r   r   r   r   r   r   r!   r   r   r   y  s    (
$*(r!   r   c                   4    e Zd ZdZd Zd Zd Zd Zd Zd Z	y)	TestReviewAllu%   전체 리뷰 결과 집계 테스트c                     |dz  }|j                  dd       t        j                  t        |      g      }d|v sJ d|v sJ d|v sJ d|d   v sJ d	|d   v sJ d
|d   v sJ y)u   review_all 결과 구조 검증r
   r   r   r   files_analyzedr   summaryr   rw   rM   Nr   r   
review_allr   r   s       r   test_review_all_structurez'TestReviewAll.test_review_all_structure  s    y 	Y1''Q16)))V###F"""VI....F9----	****r!   c                     g }t        d      D ]8  }|d| dz  }|j                  dd       |j                  t        |             : t        j                  |      }|d   dk(  sJ y)	u   분석된 파일 수 정확성r4   test_.pyr   r   r   r   N)r   r   appendr   r   r   )r   r   filesr   r   r   s         r   $test_review_all_files_analyzed_countz2TestReviewAll.test_review_all_files_analyzed_count  sr    q 	!AU1#S>)ALLWL5LLQ 	! ''.&'1,,,r!   c                     |dz  }d}|j                  |d       t        j                  t        |      g      }|d   d   dk\  sJ |d   d   dk\  sJ y	)
u'   심각도별 요약 카운트 정확성r
   7api_key = "AKIAIOSFODNN7EXAMPLE"
import os
# TODO: fix
r   r   r   r   r   rM   Nr   )r   r   r   r   r   s        r   test_review_all_summary_countsz,TestReviewAll.test_review_all_summary_counts  sg    y T	Ww/''Q1i ,111i (A---r!   c                     t         j                  g       }|d   dk(  sJ |d   g k(  sJ |d   d   dk(  sJ |d   d   dk(  sJ |d   d   dk(  sJ y)	u   빈 파일 목록 처리r   r   r   r   r   rw   rM   N)r   r   )r   r   s     r   test_review_all_empty_listz(TestReviewAll.test_review_all_empty_list  s    ''+&'1,,,j!R'''i ,111i +q000i (A---r!   c                     |dz  }|j                  dd       t        j                  t        |      g      }t	        |d   t
              sJ y)u"   findings가 리스트인지 확인r
   z# TODO: test
r   r   r   N)r   r   r   r   r   r   r   s       r   test_review_all_findings_listz+TestReviewAll.test_review_all_findings_list  sH    y 	%8''Q1&,d333r!   c                     |dz  }|j                  dd       t        j                  t        |      g      }t	        j
                  |      }t	        j                  |      }|d   dk(  sJ y)u,   결과가 JSON 직렬화 가능한지 확인r
   z!api_key = "AKIAIOSFODNN7EXAMPLE"
r   r   r   r   N)r   r   r   r   jsondumpsloads)r   r   r   r   json_strparseds         r   !test_review_all_json_serializablez/TestReviewAll.test_review_all_json_serializable  sc    y 	9GL''Q1::f%H%&'1,,,r!   N)
rD   rE   rF   rG   r   r   r   r   r   r  r   r!   r   r   r     s#    /
+-..4-r!   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(  sJ 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.pyr   r   return_valuer   get_changed_filesr   mock_outputmock_runr   s       r   test_returns_list_of_filesz.TestGetChangedFiles.test_returns_list_of_files  sg    8#$ 	B$-"%H!  11.AE	B )+;<<<<	B 	Bs   (AAc                     t        d      5 }t        dd      |_        t        j	                  d      }ddd       g k(  sJ y# 1 sw Y   xY w)u,   변경 파일 없으면 빈 리스트 반환r  r<   r   r  r	  Nr
  r   r  r   s      r   "test_empty_diff_returns_empty_listz6TestGetChangedFiles.test_empty_diff_returns_empty_list  sY    #$ 	B$-%H!  11.AE	B {{	B 	Bs   (AAc                     t        d      5 }t        j                  ddd      |_        t        j                  d      }ddd       g k(  sJ y# 1 sw Y   xY w)u.   git 실패 시 빈 리스트 반환 (graceful)r  r   gitznot a git repository)stderrr	  N)r   
subprocessCalledProcessErrorside_effectr   r  r  s      r   #test_git_failure_returns_empty_listz7TestGetChangedFiles.test_git_failure_returns_empty_list  s\    #$ 	B#-#@#@ERh#iH 11.AE	B {{	B 	Bs   3AAc                     t        d      5 }t        d      |_        t        j	                  d      }ddd       g k(  sJ y# 1 sw Y   xY w)u*   git 명령 없을 때 빈 리스트 반환r  zgit not foundr	  N)r   FileNotFoundErrorr  r   r  r  s      r   %test_git_not_found_returns_empty_listz9TestGetChangedFiles.test_git_not_found_returns_empty_list  sQ    #$ 	B#4_#EH 11.AE	B {{	B 	Bs   &AAc                     d}t        d      5 }t        |d      |_        t        j	                  d      }ddd       t        d D              sJ y# 1 sw Y   xY w)u&   Python 파일만 필터링 (선택적)z(scripts/foo.py
README.md
scripts/bar.py
r  r   r  r	  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  r   r  s       r   test_filters_non_python_filesz1TestGetChangedFiles.test_filters_non_python_files  sf    C#$ 	B$-"%H!  11.AE	B 4e4444	B 	Bs   (AAc                     t        d      5 }t        dd      |_        t        j	                         }ddd       t        t              sJ y# 1 sw Y   xY w)u   기본 diff range (HEAD) 사용r  zscripts/foo.py
r   r  N)r   r   r  r   r  r   r   r  s      r   test_default_diff_rangez+TestGetChangedFiles.test_default_diff_range  sW    #$ 	4$-)%H!  113E	4 %&&&	4 	4s   'AAN)
rD   rE   rF   rG   r  r  r  r  r!  r#  r   r!   r   r  r    s#    ,	=
5'r!   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J y)u   대용량 파일 처리r   i'  zlarge.pyr<   r   r   N)r   r   r   r   r   r   r   )r   r   r   r   r   s        r   test_very_large_filez"TestEdgeCases.test_very_large_file,  sT    e#z!	RWWU^g6**3q62(D)))r!   c                     d}|dz  }|j                  |d       t        j                  t        |            }t	        |t
              sJ y)u   주석만 있는 파일z&# This is a comment
# Another comment
zcomments.pyr   r   Nr   r   r   r   r   r   s        r   test_file_with_only_commentsz*TestEdgeCases.test_file_with_only_comments4  sE    <}$	Ww/**3q62(D)))r!   c                     d}|dz  }|j                  |d       t        j                  t        |            }t	        |t
              sJ y)u   유니코드 내용 처리u:   # 한글 주석
# TODO: 한글 TODO
x = '안녕하세요'
z
unicode.pyr   r   Nr   r)  s        r   test_unicode_contentz"TestEdgeCases.test_unicode_content<  sE    Q|#	Ww/**3q62(D)))r!   c                     d}|dz  }|j                  |j                  d             t        j                  t	        |            }t        |t              sJ y)u   Windows 줄 끝 처리zimport os
# TODO: fix
x = 1
z
windows.pyr   N)r   encoder   r   r   r   r   r)  s        r   test_windows_line_endingsz'TestEdgeCases.test_windows_line_endingsD  sJ    9|#	gnnW-.**3q62(D)))r!   c                     d}|dz  }|j                  |d       t        j                  t        |            }t	        |      dk\  sJ |D ]   }d|v sJ d|v sJ d|v sJ d	|v sJ d
|v r J  y)u   Finding 구조 완전성 검증z# TODO: test finding structurer
   r   r   r   r7   r3   r   r   r\   N)r   r   r   r   r   )r   r   r   r   r   findings         r   test_finding_structure_completez-TestEdgeCases.test_finding_structure_completeL  s    2y 	Ww/**3q628}!!! 	(GW$$$W$$$(((((('''	(r!   c                     d}|dz  }|j                  |d       t        j                  t        |            }h d}|D ]  }|d   |v rJ  y)u2   심각도 값이 유효한 범위 내인지 확인r   r
   r   r   >   rM   rw   r   r   Nr   )r   r   r   r   r   valid_severitiesr1  s          r   test_severity_values_validz(TestEdgeCases.test_severity_values_validZ  s^    Ty 	Ww/**3q62: 	;G:&*::::	;r!   N)
rD   rE   rF   rG   r'  r*  r,  r/  r2  r5  r   r!   r   r%  r%  )  s#    +****(;r!   r%  c                   (    e Zd ZdZd Zd Zd Zd Zy)TestFindingStructureu"   Finding 데이터 구조 테스트c                     d}t         j                  d|      }t        |t              sJ |rt        |d   t              sJ yy)u0   check_hardcoded_secrets가 dict 리스트 반환zapi_key = "secret123"r
   r   N)r   r   r   r   dictr   s      r   .test_check_hardcoded_secrets_returns_dict_listzCTestFindingStructure.test_check_hardcoded_secrets_returns_dict_listm  sE    )66y'J(D)))hqk4000 r!   c                     d}t         j                  d|      }t        |t              sJ |rt        |d   t              sJ yy)u$   check_todos가 dict 리스트 반환z# TODO: testr
   r   N)r   rO   r   r   r9  r   s      r   "test_check_todos_returns_dict_listz7TestFindingStructure.test_check_todos_returns_dict_listu  sE     **9g>(D)))hqk4000 r!   c                     d}t         j                  d|      }t        |t              sJ |rt        |d   t              sJ yy)u-   check_unused_imports가 dict 리스트 반환zimport os
x = 1r
   r   N)r   rm   r   r   r9  r   s      r   +test_check_unused_imports_returns_dict_listz@TestFindingStructure.test_check_unused_imports_returns_dict_list}  sE    $33IwG(D)))hqk4000 r!   c                     dj                  d t        d      D              }d| d}t        j                  d|      }t	        |t
              sJ |rt	        |d   t              sJ 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   N)r   r   r   r   r   r   r9  )r   r   r   r   s       r   ,test_check_function_length_returns_dict_listzATestFindingStructure.test_check_function_length_returns_dict_list  sg    yy?U2Y??!$'9:44YH(D)))hqk4000 r!   N)rD   rE   rF   rG   r:  r<  r>  rB  r   r!   r   r7  r7  j  s    ,1111r!   r7  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\  sJ y)u+   DB URL에 자격증명 포함 패턴 탐지z3DATABASE_URL = "postgresql://user:password@host/db"r
   r   Nr#   r   s      r   *test_detects_database_url_with_credentialszGTestAdditionalSecretPatterns.test_detects_database_url_with_credentials  s+    G66y'J8}!!!r!   c                 X    d}t         j                  d|      }t        |t              sJ y)u<   localhost URL은 탐지 안 함 (자격증명 없는 경우)z,DATABASE_URL = "postgresql://localhost/mydb"r
   Nr   r   r   r   r   s      r    test_no_false_positive_localhostz=TestAdditionalSecretPatterns.test_no_false_positive_localhost  s+    @66y'J (D)))r!   c                 T    d}t         j                  d|      }t        |      dk\  sJ y)u   Bearer 토큰 패턴 탐지zJheaders = {"Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"}r
   r   Nr#   r   s      r   test_detects_bearer_tokenz6TestAdditionalSecretPatterns.test_detects_bearer_token  s+    ^66y'J8}!!!r!   c                 X    d}t         j                  d|      }t        |t              sJ y)u$   플레이스홀더는 탐지 안 함zapi_key = "<YOUR_API_KEY>"r
   NrH  r   s      r   "test_no_false_positive_placeholderz?TestAdditionalSecretPatterns.test_no_false_positive_placeholder  s)    .66y'J(D)))r!   N)rD   rE   rF   rG   rF  rI  rK  rM  r   r!   r   rD  rD    s    2"*"*r!   rD  )$rG   importlib.util	importlibr   r  syspathlibr   unittest.mockr   r   pytest__file__parent_SCRIPTS_DIRpathinsertr   _MODULE_PATHutilspec_from_file_locationspecmodule_from_specr   loaderexec_moduler   rI   rg   r   r   r   r  r%  r7  rD  r   r!   r   <module>r`     s)  "    
  *  H~$$++ 3|$ % ..~~--m\J nn--d3{{     $L" L"hE EZI IbR Rt%( %(Z9- 9-B:' :'D9; 9;B"1 "1T* *r!   