
    NiX~                    F   d Z ddlmZ ddlZddlmZmZmZ ddlZddlm	Z	m
Z
mZm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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(d" Z)d# Z*d$ Z+d% Z,d& Z-d' Z.d( Z/d) Z0d* Z1d+ Z2d, Z3d- Z4d. Z5y)/u   Phase 3 evidence gate regression tests — T1~T16 + TU1~TU3.

모든 외부 GitHub API 호출은 mock_gh_api fixture로 stub.
실제 gh api / GEMINI_API_KEY / network 호출 0건.
    )annotationsN)datetimetimezone	timedelta)_isomake_reviewmake_issue_comment
SCRIPT_DIRc           	         d}t        j                  t        j                        } |t	        d|      gt        |t        d      z
               | j                  d|d      }|d   d	k(  sJ |       d
|d   v sJ y)u8   T1: review 1건 + SHA 일치 + severity 없음 → PASS.abc123zLGTM, looks good!
   secondsreviewshead_sha_date   
owner/repostatepassvalid evidencereasonNr   nowr   utcr   r   r   evaluate_gateevidence_modulemock_gh_apihead_shar   results        D/home/jay/workspace/tests/phase3_evidence_gate/test_evidence_gate.py test_t1_valid_review_no_severityr#      s    H
,,x||
$C0(;<32!667 **1hEF'?f$,f,$vh////    c                    t        j                  t        j                        } |t	        |t        d      z
               | j                  ddd      }|d   dk(  sJ |d	   d
k  sJ y)u:   T2: review 0건 + 마지막 push 후 1분 경과 → HOLD.<   r   r   r   r   r   r   holdelapsed_secondsi,  Nr   r   r   r   r   r   r   r   r   r   r!   s       r"   "test_t2_no_evidence_within_timeoutr,   +   se    
,,x||
$Cd32)>#>?@**1hEF'?f$$$#$s***r$   c                    t        j                  t        j                        } |t	        |t        d      z
               | j                  ddd      }|d   dk(  sJ d	|d
   j                         v sJ y)u;   T3: review 0건 + 마지막 push 후 6분 경과 → BLOCK.ih  r   r'   r   r   r   r   blocktimeoutr   N)r   r   r   r   r   r   r   lowerr+   s       r"   $test_t3_no_evidence_timeout_exceededr1   8   sm    
,,x||
$Cd33)?#?@A**1hEF'?g%%%x(..0000r$   c           	     b   d}d}t        j                  t        j                        } |t	        d|      gt        |t        d      z
               | j                  d|d      }|d	   d
k(  sJ |       d|d   j                         v sJ |d   d   }t        |      dk(  sJ |d   d   du sJ y)uM   T4: review 1건 + SHA 불일치 (force-push 후 stale) → BLOCK (all_stale).	oldsha000	newsha999zLooks fine.r   r   r   r   r   r   r.   staler   evidenceprimaryr   TN)
r   r   r   r   r   r   r   r   r0   len)r   r   old_shanew_shar   r!   r7   s          r"   $test_t4_stale_review_all_stale_blockr;   E   s    GG
,,x||
$C]G4532!667 **1g|DF'?g%-v-%fX&,,....Z +Gw<11:g$&&&r$   c           	        d}t        j                  t        j                        } |t	        d|      gt        |t        d      z
               | j                  d|d      }|d   d	k(  sJ t        d
 |d   d   D              sJ y)u3   T5: review 1건 + body에 🔴 이모지 → BLOCK.r   u   🔴 SQL injection in line 42r   r   r   r   r   r   r.   c              3  $   K   | ]  }d |v  
 ywemojiN .0hs     r"   	<genexpr>z2test_t5_high_severity_emoji_red.<locals>.<genexpr>f   s     Nw!|N   r6   high_severity_hitsN	r   r   r   r   r   r   r   r   anyr   s        r"   test_t5_high_severity_emoji_redrI   \   s    H
,,x||
$C<hGH32!667 **1hEF'?g%%%NVJ%78L%MNNNNr$   c           	     &   d}t        j                  t        j                        } |t	        d|      gt        |t        d      z
               | j                  d|d      }|d   d	k(  sJ |d
   d   }t        d |D              s
J d|        y)u5   T6: review 1건 + body에 'severity: high' → BLOCK.r   u6   severity: high — this PR introduces a critical flaw.r   r   r   r   r   r   r.   r6   rF   c              3  $   K   | ]  }d |v  
 ywseverityNr@   rA   s     r"   rD   z>test_t6_high_severity_keyword_severity_high.<locals>.<genexpr>x        -1zQ-rE   zexpected severity hit, got: NrG   r   r   r    r   r!   hitss         r"   +test_t6_high_severity_keyword_severity_highrQ   m   s    H
,,x||
$CUW_`a32!667 **1hEF'?g%%%*23D---T1MdV/TT-r$   c           	     &   d}t        j                  t        j                        } |t	        d|      gt        |t        d      z
               | j                  d|d      }|d   d	k(  sJ |d
   d   }t        d |D              s
J d|        y)u;   T7: review 1건 + body에 'BLOCKING' (대문자) → BLOCK.r   z*BLOCKING: auth bypass vulnerability found.r   r   r   r   r   r   r.   r6   rF   c              3  0   K   | ]  }d |v xs d|v   yw)BLOCKINGkeywordNr@   rA   s     r"   rD   zCtest_t7_high_severity_keyword_blocking_uppercase.<locals>.<genexpr>   s      ?QzQ0)q.0?   zexpected keyword hit, got: NrG   rO   s         r"   0test_t7_high_severity_keyword_blocking_uppercaserW      s    H
,,x||
$CI8TU32!667 **1hEF'?g%%%*23D?$??eC^_c^dAee?r$   c           	     *   d}t        j                  t        j                        }d} |t	        ||      gt        |t        d      z
               | j                  d|d      }|d   d	k(  sJ |d
   d   }t        d |D              s
J d|        y)u8   T8: review 1건 + body에 '## High' h2 헤더 → BLOCK.r   z=Code review results:

## High
- SQL injection risk at line 10r   r   r   r   r   r   r.   r6   rF   c              3  $   K   | ]  }d |v  
 ywheaderNr@   rA   s     r"   rD   z7test_t8_high_severity_header_h2_high.<locals>.<genexpr>        +x1}+rE   zexpected header hit, got: NrG   )r   r   r    r   bodyr!   rP   s          r"   $test_t8_high_severity_header_h2_highr^      s    H
,,x||
$CMDT8,-32!667 **1hEF'?g%%%*23D+d++P/I$-PP+r$   c           	        d}t        j                  t        j                        } |t	        d|      gt        |t        d      z
               | j                  d|d      }|d   d	k(  sJ d
|d   d   v sJ |d   d   g k(  sJ y)uG   T9: review + 'security' 단독 보조 신호만 → PASS, audit 기록.r   z&This change has security implications.r   r   r   r   r   r   r   securityr6   supplementary_signalsrF   Nr   r   s        r"   'test_t9_supplementary_security_no_blockrb      s    H
,,x||
$CExPQ32!667 **1hEF'?f$$$
+,CDDDD*23r999r$   c           	         d}t        j                  t        j                        }d} |t	        ||      gt        |t        d      z
               | j                  d|d      }|d   d	k(  sJ |d
   d   g k(  sJ y)u;   T10: code block 안 BLOCKING은 차단 패턴에서 제외.r   zCLooks good!
```python
# BLOCKING comment in code
print('hello')
```r   r   r   r   r   r   r   r6   rF   Nr   )r   r   r    r   r]   r!   s         r"   test_t10_code_block_excludedrd      s    H
,,x||
$CTDT8,-32!667 **1hEF'?f$$$*23r999r$   c           	        t        j                  t        j                        }|t	        d      z
  }|t	        d      z
  } |g t        dt        |            gt        |             | j                  ddd	      }|d
   dk(  sJ |       d|d   v sJ |d   d   }t        |      dk(  sJ |d   d   dk(  sJ |d   d   du sJ y)u   T11: issue_comment만 1건 (review 0건) + severity 없음 → PASS.

    issue_comment는 created_at >= head_pushed_at 일 때만 valid primary evidence.
       r      z(Gemini review complete. No issues found.
created_atr   issue_commentsr   r   r   r   r   r   r   r   r6   r7   r   typeissue_commentr5   FN)	r   r   r   r   r   r	   r   r   r8   )r   r   r   head_pushed_atcomment_created_atr!   r7   s          r"   ,test_t11_issue_comment_only_no_severity_passrp      s    
 ,,x||
$C9R00Ny33*6./
  >* **1hEF'?f$,f,$vh////Z +Gw<11:f0001:g%'''r$   c           	     n   t        j                  t        j                        }|t	        d      z
  }|t	        d      z
  } |g t        dt        |            gt        |             | j                  ddd	      }|d
   dk(  sJ |       d|d   j                         v sJ |d   d   d   d   du sJ y)u   T11b: issue_comment의 created_at이 head push 이전이면 stale → BLOCK (all_stale).

    force-push 시나리오: 새 SHA가 push된 후 옛 issue comment만 남아있는 상황.
    rf   r   iX  LGTMrh   rj   r   	newsha456r   r   r.   r5   r   r6   r7   r   TN)	r   r   r   r   r   r	   r   r   r0   )r   r   r   rn   old_comment_created_atr!   s         r"   1test_t11b_issue_comment_before_head_push_is_staleru      s    
 ,,x||
$C9R00N 9S#99*23
  >* **1k<HF'?g%-v-%fX&,,....*i(+G4<<<r$   c                    dddddidddddidd	d
ddidgi|j                  | dfd       | j                  dd      }t        |      dk(  s
J d|        |d   d   dk(  sJ y)u   T11c: _fetch_check_runs는 app.slug == 'gemini-code-assist' 정확 매칭만 인정.

    이름이 'gemini-something'으로 시작해도 app.slug 다르면 필터링됨.
    
check_runsr   z
gemini-fooslugz	other-bot)idnameapp   zgemini-code-assist   zGemini Manual Checkzmanual-runner_gh_apic                    dfS )Nr   r@   )	_endpoint_kfake_responses     r"   <lambda>z<test_t11c_fetch_check_runs_strict_app_slug.<locals>.<lambda>  s    QP]L^ r$   r   r   z-Only gemini-code-assist app should pass: got r   ry   N)setattr_fetch_check_runsr8   )r   monkeypatchr!   r   s      @r"   *test_t11c_fetch_check_runs_strict_app_slugr      s     	lFK3HI2FDX;YZ3V_<UV
M 4^_..|XFFv;!ULVHUU!9T?ar$   c           	        |j                  dd       d}t        j                  t        j                        } |t        d|      gt        |t        d      z
               | j                  d	|d
      }|d   dk(  sJ y)uD   T12: GEMINI_API_KEY 누락 상태에서도 정상 동작 (의존 0).GEMINI_API_KEYF)raisingr   rr   r   r   r   r   r   r   r   N)	delenvr   r   r   r   r   r   r   r   )r   r   r   r    r   r!   s         r"   #test_t12_no_gemini_api_key_requiredr     s|    '7H
,,x||
$CVX./32!667 **1hEF'?f$$$r$   c                    d}t        j                  t        j                        } |t	        |t        d      z
               | j                  d|d      }|d   dk(  sJ |d	   d
k  sJ y)uM   T13: force-push 후 새 SHA — head_pushed_at 새로 갱신되면 elapsed=0.newforce123rg   r   r'   r   r   r   r(   r)   r&   Nr*   )r   r   r:   r   r!   s        r"    test_t13_force_push_resets_timerr     sj    G
,,x||
$Cd31)=#=>?**1g|DF'?f$$$#$r)))r$   c           	     6   d}t        j                  t        j                        } |t	        d|      gt        |t        d      z
               |j                  d|d      }|j                  d|d      }|d   |d   cxk(  rd	k(  sJ  J |d
   |d
   k(  sJ y)uH   T14: 두 check name이 같은 evaluate_gate를 호출 — 결과 동일.r   rr   r   r   r   r   r   r   r   r   Nr   )gate_moduler   r   r    r   r1r2s          r"   $test_t14_two_check_names_same_resultr   .  s    H
,,x||
$CVX./32!667 
	&	&q(L	AB		&	&q(L	ABg;"W+//////h<2h<'''r$   c                  	 d}t        j                  t        j                        } |t	        d|      gt        |t        d      z
               g 	d	fd	}|j                  | d|       dD ]B  }|j                  t        d	d
ddd|ddd|dg
       | j                         }|dk(  r:J d|         t        	      dk(  sJ 	d   d   dk(  sJ 	d   d   dk(  sJ 	d   d   	d   d   cxk(  rdk(  sJ  J 	d   d   	d   d   k(  sJ 	d   d   	d   d   cxk(  rdk(  sJ  J 	d   d   	d   d   cxk(  r|k(  sJ  J y) u  T14b: gemini_review_gate.py CLI를 두 check name(--check-name)으로 실행했을 때
    publish_check_run에 전달되는 payload가 동일해야 함 (state/conclusion/summary).

    실제 wrapper가 동일 evaluate_gate를 호출하는지 CLI 레벨로 보장.
    r   rr   r   r   r   c                >    j                  | ||||d       ddddS )N)reposharz   
conclusionsummaryr   {} rcstdoutstderr)append)r   r   rz   r   r   detailscaptureds         r"   fake_publishzHtest_t14b_cli_two_check_names_same_publish_payload.<locals>.fake_publishK  s/    d$
 	 4266r$   publish_check_run)gemini-review-gatephase3-merge-gateargvgemini_review_gate.py--pr-number1--commit-sha--repor   --check-name--publish-checkr   zPASS state should return 0 for r|   rz   r   r   r   r   successr   r   r   Nr   )r   r   r   r   r   r   r   r   sysmainr8   )
r   r   r   r   r    r   r   
check_namer   r   s
            @r"   2test_t14b_cli_two_check_names_same_publish_payloadr   <  s    H
,,x||
$CVX./32!667
 H7 %8,GA G
C#3lNJ	*
 	 QwF9*FFwG x=AA;v"6666A;v"5555A;|$L(ANYNNNNNA;y!Xa[%;;;;A;v(1+f"5EEEEEEA;u!U!3?x?????r$   c                    ddl } ddl}g d}d}t        dz  t        dz  g}|D ]  }|j                  d      }	 |j	                  |t        |      	      }t               }	|j                        D ]k  }
t        |
|j                        st        |
j                  t
              s5|
j                  }t!        |
d|      }|	j#                  t%        ||dz                m |j'                         }t)        |d      D ]  \  }}|j+                         }|j-                  d      r(||	v r-|D ]?  }| j/                  ||      st        j                  |j                   d| d| d|       A | j/                  ||      st        j                  |j                   d| d|         y# t        $ r/}t        j                  |j                   d
|        Y d}~d}~ww xY w)uU   T15: 소스 코드에 generativelanguage.googleapis.com 등 Gemini API endpoint 0건.r   N)z#generativelanguage\.googleapis\.comzgoogleapis\.com.*modelszgoogle\.generativeaizgenai\.GenerativeModelz>(?:os\.environ|os\.getenv)\s*[\[\(]\s*["\']GEMINI_API_KEY["\']zgemini_evidence_verify.pyr   zutf-8)encoding)filenamez: SyntaxError during parse: 
end_linenor   #:z forbidden pattern 'z': z& forbidden GEMINI_API_KEY env access: )reastr
   	read_textparsestrSyntaxErrorpytestfailrz   setwalk
isinstanceConstantvaluelinenogetattrupdaterange
splitlines	enumeratestrip
startswithsearch)_re_astforbidden_patternsenv_access_pattern	src_filessrccontenttreeestring_line_rangesnodestartendlinesln_nolinestrippedpatterns                     r"   )test_t15_no_gemini_api_endpoint_in_sourcer   l  s    [ 	00,,I
  &---1	F::gC:9D
 (+uIIdO 	AD$.:djj#3NdL%8"))%sQw*?@		A ""$$UA. 	KE4zz|H""3'** . ::gt,KK88*AeW,@	THU zz,d3xxj%(NthW%	%&  	FKK388*$@DEE	Fs   F,,	G$5$GG$c                t  	 d}t        j                  t        j                        } |t	        |t        d      z
               i 	d	fd	}|j                  | d|       |j                  t        ddd	d
d|dddddg
       | j                         }|dk(  sJ d       	d   dk(  s
J d	        d	d   v sJ y)uN   T16: HOLD state → GitHub check conclusion='failure' (neutral 절대 금지).r   r&   r   r'   c                :    j                  |||d       ddddS )N)r   r   rz   r   r   r   r   )r   )r   r   rz   r   r   r   	publisheds         r"   r   z?test_t16_hold_maps_to_failure_not_neutral.<locals>.fake_publish  s&    
wPTUV4266r$   r   r   r   r   r   r   r   r   r   r   r   r   z%HOLD should return non-zero exit coder   failurez0HOLD must map to 'failure', not 'neutral'. got: HOLDr   Nr   )	r   r   r   r   r   r   r   r   r   )
r   r   r   r   capsysr    r   r   r   r   s
            @r"   )test_t16_hold_maps_to_failure_not_neutralr     s    H
,,x||
$Cd32)>#>?@ I7 %8,GVsNH,0D	&  
			B7;;;7\"i/ 
:9+F/ Yy))))r$   c                N    d}| j                  |      }d|vsJ d|v sJ d|v sJ y)uH   TU1-a: fenced code block (``` ... ```) 내용이 제거되는지 확인.z8Normal text
```python
BLOCKING code here
```
After blockrT   zNormal textzAfter blockNstrip_code_blocksr   r]   r   s      r"   !test_tu1_strip_code_blocks_fencedr     sA    ID006HX%%%H$$$H$$$r$   c                N    d}| j                  |      }d|vsJ d|v sJ d|v sJ y)u>   TU1-b: inline code (` ... `) 내용이 제거되는지 확인.z Use `BLOCKING` method carefully.rT   Use	carefullyNr   r   s      r"   !test_tu1_strip_code_blocks_inliner     sA    -D006HX%%%H("""r$   c                V    | j                  d      dk(  sJ | j                  d      J y)u,   TU1-c: 빈 body 입력 시 그대로 반환.r   Nr   r   s    r"    test_tu1_strip_code_blocks_emptyr     s2    ,,R0B666,,T2:::r$   c                8    d}| j                  |      }||k(  sJ y)uB   TU1-d: code block 없는 일반 텍스트는 변경 없이 반환.z This is a normal review comment.Nr   r   s      r"   "test_tu1_strip_code_blocks_no_coder     s%    -D006Htr$   c                ^    | j                  d      }t        d |D              s
J d|        y)u.   TU2-a: 🔴 이모지 → emoji 패턴 매칭.u   🔴 critical issue foundc              3  $   K   | ]  }d |v  
 ywr>   r@   rA   s     r"   rD   z9test_tu2_match_high_severity_emoji_red.<locals>.<genexpr>       *w!|*rE   got: Nmatch_high_severityrH   r   rP   s     r"   &test_tu2_match_high_severity_emoji_redr    s2    ../JKD*T**:eD6N:*r$   c                ^    | j                  d      }t        d |D              s
J d|        y)u-   TU2-b: ❌ 이모지 → emoji 패턴 매칭.u   ❌ do not mergec              3  $   K   | ]  }d |v  
 ywr>   r@   rA   s     r"   rD   z7test_tu2_match_high_severity_emoji_x.<locals>.<genexpr>  r   rE   r   Nr   r   s     r"   $test_tu2_match_high_severity_emoji_xr    s2    ../ABD*T**:eD6N:*r$   c                ^    | j                  d      }t        d |D              s
J d|        y)u7   TU2-c: 'severity: critical' → severity 패턴 매칭.zseverity: critical bug herec              3  $   K   | ]  }d |v  
 ywrL   r@   rA   s     r"   rD   z@test_tu2_match_high_severity_severity_keyword.<locals>.<genexpr>  rN   rE   r   Nr   r   s     r"   -test_tu2_match_high_severity_severity_keywordr    s2    ../LMD---=tf~=-r$   c                ^    | j                  d      }t        d |D              s
J d|        y)u6   TU2-d: 'BLOCKING' 키워드 → keyword 패턴 매칭.zBLOCKING issue: auth bypassc              3  0   K   | ]  }d |v xs d|v   yw)rU   rT   Nr@   rA   s     r"   rD   z@test_tu2_match_high_severity_blocking_keyword.<locals>.<genexpr>  s      ?QyA~0q0?rV   r   Nr   r   s     r"   -test_tu2_match_high_severity_blocking_keywordr
    s2    ../LMD?$??O5O?r$   c                ^    | j                  d      }t        d |D              s
J d|        y)u6   TU2-e: 'MUST FIX' 키워드 → keyword 패턴 매칭.zMUST FIX before mergec              3  0   K   | ]  }d |v xs d|v   yw)rU   MUSTNr@   rA   s     r"   rD   z@test_tu2_match_high_severity_must_fix_keyword.<locals>.<genexpr>  s      ;yA~,1,;rV   r   Nr   r   s     r"   -test_tu2_match_high_severity_must_fix_keywordr    s2    ../FGD;d;;KuTF^K;r$   c                b    d}| j                  |      }t        d |D              s
J d|        y)u4   TU2-f: '## High' h2 헤더 → header 패턴 매칭.z Review:

## High
- SQL injectionc              3  $   K   | ]  }d |v  
 ywrZ   r@   rA   s     r"   rD   z;test_tu2_match_high_severity_header_high.<locals>.<genexpr>  r\   rE   r   Nr   r   r]   rP   s      r"   (test_tu2_match_high_severity_header_highr    s6    0D..t4D+d++;uTF^;+r$   c                b    d}| j                  |      }t        d |D              s
J d|        y)u8   TU2-g: '## Blocking' h2 헤더 → header 패턴 매칭.z'## Blocking
Must be fixed before merge.c              3  $   K   | ]  }d |v  
 ywrZ   r@   rA   s     r"   rD   z?test_tu2_match_high_severity_header_blocking.<locals>.<genexpr>  r\   rE   r   Nr   r  s      r"   ,test_tu2_match_high_severity_header_blockingr    s6    5D..t4D+d++;uTF^;+r$   c                4    | j                  d      }|g k(  sJ y)u:   TU2-h: severity 없는 일반 텍스트 → 빈 리스트.zLGTM, code looks cleanNr   r   s     r"   %test_tu2_match_high_severity_no_matchr    s    ../GHD2::r$   c                ^    | j                  d      }t        d |D              r
J d|        y)uF   TU2-i: 'blocking' 소문자는 매칭 안됨 (정확히 대문자만).z$This might be blocking the pipeline.c              3  $   K   | ]  }d |v  
 yw)rU   Nr@   rA   s     r"   rD   zKtest_tu2_match_high_severity_blocking_lowercase_no_match.<locals>.<genexpr>&  s     0a9>0rE   zunexpected keyword match: Nr   r   s     r"   8test_tu2_match_high_severity_blocking_lowercase_no_matchr  "  s7    ../UVD0400U4Ntf2UU00r$   c                \    | j                  d      g k(  sJ | j                  d      g k(  sJ y)u"   TU2-j: 빈 body → 빈 리스트.r   Nr  r   s    r"   "test_tu2_match_high_severity_emptyr  )  4    ..r2b888..t4:::r$   c                B    | j                  d      }d|v s
J d|        y)u4   TU3-a: 'security' 단어 → security 보조 신호.zThis has security implications.r`   r   Nmatch_supplementaryr   sigss     r"   %test_tu3_match_supplementary_securityr$  3  s,    ../PQD-tf~-r$   c                B    | j                  d      }d|v s
J d|        y)u6   TU3-b: 'data loss' 구문 → data_loss 보조 신호.z!Risk of data loss if not handled.	data_lossr   Nr   r"  s     r"   &test_tu3_match_supplementary_data_lossr'  9  s,    ../RSD$.%v.r$   c                B    | j                  d      }d|v s
J d|        y)u8   TU3-c: 'regression' 단어 → regression 보조 신호.zThis could cause a regression.
regressionr   Nr   r"  s     r"   'test_tu3_match_supplementary_regressionr*  ?  s,    ../OPD4/5/r$   c                N    d}| j                  |      }d|v sJ d|v sJ d|v sJ y)u-   TU3-d: 여러 보조 신호 동시에 존재.z>There's a security issue and risk of data loss and regression.r`   r&  r)  Nr   r   r]   r#  s      r"   %test_tu3_match_supplementary_multipler-  E  sA    KD..t4D$4r$   c                4    | j                  d      }|g k(  sJ y)u8   TU3-e: 보조 신호 없는 텍스트 → 빈 리스트.zLGTM, everything looks fine.Nr   r"  s     r"   %test_tu3_match_supplementary_no_matchr/  N  s    ../MND2::r$   c                F    d}| j                  |      }d|vs
J d|        y)u@   TU3-f: code block 안 'security' → 보조 신호에서 제외.z(```python
# security check here
pass
```r`   z2expected no security signal from code block, got: Nr   r,  s      r"   3test_tu3_match_supplementary_in_code_block_excludedr1  T  s4    8D..t4DT!^%WX\W]#^^!r$   c                \    | j                  d      g k(  sJ | j                  d      g k(  sJ y)u"   TU3-g: 빈 body → 빈 리스트.r   Nr   r   s    r"   "test_tu3_match_supplementary_emptyr3  [  r  r$   c                B    | j                  d      }d|v s
J d|        y)u0   TU3-h: 'Security' 대소문자 혼합도 매칭.zSecurity concern noted.r`   r   Nr   r"  s     r"   -test_tu3_match_supplementary_case_insensitiver5  a  s,    ../HID-tf~-r$   )6__doc__
__future__r   r   r   r   r   r   #tests.phase3_evidence_gate.conftestr   r   r	   r
   r#   r,   r1   r;   rI   rQ   rW   r^   rb   rd   rp   ru   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r
  r  r  r  r  r  r  r$  r'  r*  r-  r/  r1  r3  r5  r@   r$   r"   <module>r9     s   
 # 
 2 2  
0"+1'.
O"U$f$Q&:$:$(2=, .
%"*()@`9@*B%#;;;>PL<<V;./0 _;.r$   