
    NiX~                    `   d Z ddlmZ ddlZddlmc 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# Z0d$ Z1d% Z2d& Z3d' Z4d( Z5d) Z6d* Z7d+ Z8d, Z9d- Z:d. Z;y)/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(  }|st        j                  d
|fd||f      t        j                  |      t        j                  |      dz  }t        j                  |      dz   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)u8   T1: review 1건 + SHA 일치 + severity 없음 → PASS.abc123zLGTM, looks good!
   secondsreviewshead_sha_date   
owner/repostatepass==z%(py1)s == %(py4)spy1py4
>assert %(py6)spy6Nvalid evidencereasoninz%(py1)s in %(py4)sassert %(py6)s)r   nowr   utcr   r   r   evaluate_gate
@pytest_ar_call_reprcompare	_saferepr_format_assertmsgAssertionError_format_explanation
evidence_modulemock_gh_apihead_shar%   result@py_assert0@py_assert3@py_assert2@py_format5@py_format7s
             D/home/jay/workspace/tests/phase3_evidence_gate/test_evidence_gate.py test_t1_valid_review_no_severityr9      s   H
,,x||
$C0(;<32!667 **1hEF'?,f,?f$,,,?f,,,?,,,f,,,f,,,,,,,/vh//////////////////////    c                   t        j                  t        j                        } |t	        |t        d      z
               | j                  d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}}|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:   T2: review 0건 + 마지막 push 후 1분 경과 → HOLD.<   r   r   r   r   r   r   holdr   r   r   r$   r   Nelapsed_secondsi,  <z%(py1)s < %(py4)sr   r%   r   r&   r   r   r'   r(   r)   r*   r,   r-   )	r/   r0   r%   r2   r3   r4   r5   r6   r7   s	            r8   "test_t2_no_evidence_within_timeoutrD   +   s    
,,x||
$Cd32)>#>?@**1hEF'?$f$?f$$$$?f$$$?$$$f$$$$$$$#$*s*$s****$s***$***s*******r:   c                *   t        j                  t        j                        } |t	        |t        d      z
               | j                  d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}}d}|d   }|j                  }	 |	       }
||
v }|st        j                  d|fd||
f      t        j                  |      t        j                  |      t        j                  |	      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}x}x}x}	}
y)u;   T3: review 0건 + 마지막 push 후 6분 경과 → BLOCK.ih  r   r=   r   r   r   r   blockr   r   r   r$   r   Ntimeoutr    r!   zD%(py1)s in %(py8)s
{%(py8)s = %(py6)s
{%(py6)s = %(py4)s.lower
}()
}r   r   r   py8assert %(py10)spy10)r   r%   r   r&   r   r   r'   r(   r)   r*   r,   r-   lower)r/   r0   r%   r2   r3   r4   r5   r6   r7   @py_assert5@py_assert7@py_format9@py_format11s                r8   $test_t3_no_evidence_timeout_exceededrR   8   s   
,,x||
$Cd33)?#?@A**1hEF'?%g%?g%%%%?g%%%?%%%g%%%%%%%0x(0(..0.00900000900009000(000.000000000000r:   c           	        d}d}t        j                  t        j                        } |t	        d|      gt        |t        d      z
               | j                  d|d      }|d	   }d
}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	t        j                  |      dz   d|	iz  }
t        t        j                  |
            dx}x}}d}|d   }|j                  } |       }||v }|st        j                  d|fd||f      t        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}x}}|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}}|d   d   }d }||u }|slt        j                  d!|fd"||f      t        j                  |      t        j                  |      dz  }	d#d|	iz  }
t        t        j                  |
            dx}x}}y)$uM   T4: review 1건 + SHA 불일치 (force-push 후 stale) → BLOCK (all_stale).	oldsha000	newsha999zLooks fine.r   r   r   r   r   r   rF   r   r   r   r   r   Nstaler    r!   rH   rI   rK   rL   evidenceprimaryz0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenpy0r   py3r   assert %(py8)srJ   r   Tisz%(py1)s is %(py4)sr$   )r   r%   r   r&   r   r   r   r'   r(   r)   r*   r+   r,   r-   rM   rZ   @py_builtinslocals_should_repr_global_name)r/   r0   old_shanew_shar%   r2   r3   r4   r5   r6   r7   rN   rO   rP   rQ   rX   @py_assert4s                    r8   $test_t4_stale_review_all_stale_blockrh   E   s$   GG
,,x||
$C]G4532!667 **1g|DF'?-g-?g%---?g---?---g---v-------.fX&.&,,.,..7.....7....7...&...,...........Z +Gw<1<1<133ww<11:g&$&$&&&&$&&&&&&$&&&&&&&r:   c           	     4   d}t        j                  t        j                        } |t	        d|      gt        |t        d      z
               | 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}}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)u3   T5: review 1건 + body에 🔴 이모지 → BLOCK.r   u   🔴 SQL injection in line 42r   r   r   r   r   r   rF   r   r   r   r$   r   Nc              3  $   K   | ]  }d |v  
 ywemojiN .0hs     r8   	<genexpr>z2test_t5_high_severity_emoji_red.<locals>.<genexpr>f   s     Nw!|N   rW   high_severity_hitsz,assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}anyr\   py2r   )r   r%   r   r&   r   r   r   r'   r(   r)   r*   r,   r-   rt   rb   rc   rd   )r/   r0   r1   r%   r2   r3   r4   r5   r6   r7   @py_assert1s              r8   test_t5_high_severity_emoji_redrx   \   s   H
,,x||
$C<hGH32!667 **1hEF'?%g%?g%%%%?g%%%?%%%g%%%%%%%NVJ%78L%MNN3NNNNNNNNN3NNN3NNNNNNNNNNNNNNr:   c           	     j   d}t        j                  t        j                        } |t	        d|      gt        |t        d      z
               | 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}}|d   d   }
d |
D        }t        |      }|st        j                  d|
       dz   dt        j                          v st        j"                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}y)u5   T6: review 1건 + body에 'severity: high' → BLOCK.r   u6   severity: high — this PR introduces a critical flaw.r   r   r   r   r   r   rF   r   r   r   r$   r   NrW   rs   c              3  $   K   | ]  }d |v  
 ywseverityNrm   rn   s     r8   rq   z>test_t6_high_severity_keyword_severity_high.<locals>.<genexpr>x        -1zQ-rr   zexpected severity hit, got: .
>assert %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}rt   ru   r   r%   r   r&   r   r   r   r'   r(   r)   r*   r,   r-   rt   r+   rb   rc   rd   r/   r0   r1   r%   r2   r3   r4   r5   r6   r7   hitsrw   s               r8   +test_t6_high_severity_keyword_severity_highr   m   s(   H
,,x||
$CUW_`a32!667 **1hEF'?%g%?g%%%%?g%%%?%%%g%%%%%%%*23D--T3--T-TT1MdV/TTTTTTT3TTT3TTT-TTT-TTTTTTr:   c           	     j   d}t        j                  t        j                        } |t	        d|      gt        |t        d      z
               | 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}}|d   d   }
d |
D        }t        |      }|st        j                  d|
       dz   dt        j                          v st        j"                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}y)u;   T7: review 1건 + body에 'BLOCKING' (대문자) → BLOCK.r   z*BLOCKING: auth bypass vulnerability found.r   r   r   r   r   r   rF   r   r   r   r$   r   NrW   rs   c              3  0   K   | ]  }d |v xs d|v   yw)BLOCKINGkeywordNrm   rn   s     r8   rq   zCtest_t7_high_severity_keyword_blocking_uppercase.<locals>.<genexpr>   s      ?QzQ0)q.0?   zexpected keyword hit, got: r~   rt   ru   r   r   s               r8   0test_t7_high_severity_keyword_blocking_uppercaser      s)   H
,,x||
$CI8TU32!667 **1hEF'?%g%?g%%%%?g%%%?%%%g%%%%%%%*23D?$?e3??e?eeC^_c^dAeeeeeee3eee3eee?eee?eeeeeer:   c           	     n   d}t        j                  t        j                        }d} |t	        ||      gt        |t        d      z
               | 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}}|d   d   }d |D        }t        |      }|st        j                  d|       dz   dt        j                          v st        j"                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }	t        t        j                  |	            dx}}y)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   rF   r   r   r   r$   r   NrW   rs   c              3  $   K   | ]  }d |v  
 ywheaderNrm   rn   s     r8   rq   z7test_t8_high_severity_header_h2_high.<locals>.<genexpr>        +x1}+rr   zexpected header hit, got: r~   rt   ru   r   )r/   r0   r1   r%   bodyr2   r3   r4   r5   r6   r7   r   rw   s                r8   $test_t8_high_severity_header_h2_highr      s+   H
,,x||
$CMDT8,-32!667 **1hEF'?%g%?g%%%%?g%%%?%%%g%%%%%%%*23D+d+P3++P+PP/I$-PPPPPPP3PPP3PPP+PPP+PPPPPPr:   c           	        d}t        j                  t        j                        } |t	        d|      gt        |t        d      z
               | 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}}d}|d   d   }||v }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}|d   d   }g }||k(  }|slt        j                  d
|fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }	t        t        j                  |	            dx}x}}y)uG   T9: review + 'security' 단독 보조 신호만 → PASS, audit 기록.r   z&This change has security implications.r   r   r   r   r   r   r   r   r   r   r$   r   NsecurityrW   supplementary_signalsr!   r#   rs   r   r%   r   r&   r   r   r   r'   r(   r)   r*   r,   r-   r.   s
             r8   'test_t9_supplementary_security_no_blockr      sU   H
,,x||
$CExPQ32!667 **1hEF'?$f$?f$$$$?f$$$?$$$f$$$$$$$D
+,CDD:DDDDD:DDDD:DDDDDDDDDDD*239r93r99993r9993999r9999999r:   c           	        d}t        j                  t        j                        }d} |t	        ||      gt        |t        d      z
               | 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}}|d   d   }g }||k(  }|slt        j                  d
|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}y)u;   T10: code block 안 BLOCKING은 차단 패턴에서 제외.r   zCLooks good!
```python
# BLOCKING comment in code
print('hello')
```r   r   r   r   r   r   r   r   r   r   r$   r   NrW   rs   r   )r/   r0   r1   r%   r   r2   r3   r4   r5   r6   r7   s              r8   test_t10_code_block_excludedr      s   H
,,x||
$CTDT8,-32!667 **1hEF'?$f$?f$$$$?f$$$?$$$f$$$$$$$*239r93r99993r9993999r9999999r:   c           	     b   t        j                  t        j                        }|t	        d      z
  }|t	        d      z
  } |g t        dt        |            gt        |             | j                  ddd	      }|d
   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	t        j                  |      dz   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   }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"}||u }|slt        j                  d#|fd$||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}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   r   r   r   Nr   r    r!   r#   r$   rW   rX   rY   rZ   r[   r^   rJ   r   typeissue_commentrV   Fr_   ra   )r   r%   r   r&   r   r	   r   r'   r(   r)   r*   r+   r,   r-   rZ   rb   rc   rd   )r/   r0   r%   head_pushed_atcomment_created_atr2   r3   r4   r5   r6   r7   rX   rN   rg   rP   s                  r8   ,test_t11_issue_comment_only_no_severity_passr      sn   
 ,,x||
$C9R00Ny33*6./
  >* **1hEF'?,f,?f$,,,?f,,,?,,,f,,,f,,,,,,,/vh//////////////////////Z +Gw<1<1<133ww<11:f00000000000000000001:g'%'%''''%''''''%'''''''r:   c           	        t        j                  t        j                        }|t	        d      z
  }|t	        d      z
  } |g t        dt        |            gt        |             | j                  ddd	      }|d
   }d}||k(  }|st        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	t        j                  |      dz   d|	iz  }
t        t        j                  |
            dx}x}}d}|d   }|j                  } |       }||v }|st        j                  d|fd||f      t        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}x}}|d   d   d   d   }d}||u }|slt        j                  d|fd||f      t        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            dx}x}}y) u   T11b: issue_comment의 created_at이 head push 이전이면 stale → BLOCK (all_stale).

    force-push 시나리오: 새 SHA가 push된 후 옛 issue comment만 남아있는 상황.
    r   r   iX  LGTMr   r   r   	newsha456r   r   rF   r   r   r   r   r   NrV   r    r!   rH   rI   rK   rL   rW   rX   r   Tr_   ra   r$   )r   r%   r   r&   r   r	   r   r'   r(   r)   r*   r+   r,   r-   rM   )r/   r0   r%   r   old_comment_created_atr2   r3   r4   r5   r6   r7   rN   rO   rP   rQ   s                  r8   1test_t11b_issue_comment_before_head_push_is_staler      s   
 ,,x||
$C9R00N 9S#99*23
  >* **1k<HF'?-g-?g%---?g---?---g---v-------.fX&.&,,.,..7.....7....7...&...,...........*i(+G4<<4<<<<4<<<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t        j                  d|fd||f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }t        j                  d|       dz   d|iz  }t        t        j                  |            dx}x}}|d   d   }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   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   rm   )	_endpoint_kfake_responses     r8   <lambda>z<test_t11c_fetch_check_runs_strict_app_slug.<locals>.<lambda>  s    QP]L^ r:   r   r   r   rY   rZ   r2   r[   z-Only gemini-code-assist app should pass: got z
>assert %(py8)srJ   Nr   r   r   r   r$   r   )setattr_fetch_check_runsrZ   r(   r)   rb   rc   rd   r*   r+   r,   r-   )r/   monkeypatchr2   r5   rN   rg   r7   rP   r3   r4   r6   r   s              @r8   *test_t11c_fetch_check_runs_strict_app_slugr      s\    	lFK3HI2FDX;YZ3V_<UV
M 4^_..|XFFv;U!U;!UUU;!UUUUUU3UUU3UUUUUUvUUUvUUU;UUU!UUULVHUUUUUUUU!9T?a?a?a?ar:   c           	        |j                  dd       d}t        j                  t        j                        } |t        d|      gt        |t        d      z
               | 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)uD   T12: GEMINI_API_KEY 누락 상태에서도 정상 동작 (의존 0).GEMINI_API_KEYF)raisingr   r   r   r   r   r   r   r   r   r   r   r   r$   r   N)delenvr   r%   r   r&   r   r   r   r'   r(   r)   r*   r,   r-   )r/   r0   r   r1   r%   r2   r3   r4   r5   r6   r7   s              r8   #test_t12_no_gemini_api_key_requiredr     s    '7H
,,x||
$CVX./32!667 **1hEF'?$f$?f$$$$?f$$$?$$$f$$$$$$$r:   c                   d}t        j                  t        j                        } |t	        |t        d      z
               | 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}}|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)uM   T13: force-push 후 새 SHA — head_pushed_at 새로 갱신되면 elapsed=0.newforce123r   r   r=   r   r   r   r>   r   r   r   r$   r   Nr?   r<   r@   rB   rC   )
r/   r0   rf   r%   r2   r3   r4   r5   r6   r7   s
             r8    test_t13_force_push_resets_timerr     s    G
,,x||
$Cd31)=#=>?**1g|DF'?$f$?f$$$$?f$$$?$$$f$$$$$$$#$)r)$r))))$r)))$)))r)))))))r:   c           	     B   d}t        j                  t        j                        } |t	        d|      gt        |t        d      z
               |j                  d|d      }|j                  d|d      }|d   }|d   }||k(  }	d	}
||
k(  }|	r|st        j                  d
|	|fd|||
f      t        j                  |      t        j                  |      t        j                  |
      dz  }dd|iz  }t        t        j                  |            dx}x}	x}x}}
|d   }|d   }||k(  }	|	slt        j                  d|	fd||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}	}y)uH   T14: 두 check name이 같은 evaluate_gate를 호출 — 결과 동일.r   r   r   r   r   r   r   r   r   r   r   %(py1)s == %(py5)sz%(py5)s == %(py7)sr   py5py7assert %(py9)spy9Nr    r   r   r   r$   r   r   )gate_moduler/   r0   r1   r%   r1r2r3   rg   r5   @py_assert6r4   @py_format8@py_format10r6   r7   s                   r8   $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                b   d}t        j                  t        j                        } |t	        d|      gt        |t        d      z
               g d7fd	}|j                  | d|       dD ]  }|j                  t        d	d
ddd|ddd|dg
       | j                         }d}	||	k(  }
|
st        j                  d|
fd||	f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      dz  }t        j                   d|       dz   d|iz  }t#        t        j$                  |            dx}
}	 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}	}d   d*   }d(   d*   }||k(  }	d+}||k(  }|	r|st        j                  d,|	|fd-|||f      t        j                  |      t        j                  |      t        j                  |      d.z  }d/d0|iz  }t#        t        j$                  |            dx}x}	x}x}}d   d1   }d(   d1   }||k(  }	|	slt        j                  d|	fd$||f      t        j                  |      t        j                  |      d%z  }d&d'|iz  }t#        t        j$                  |            dx}x}	}d   d2   }d(   d2   }||k(  }	d}||k(  }|	r|st        j                  d,|	|fd-|||f      t        j                  |      t        j                  |      t        j                  |      d.z  }d/d0|iz  }t#        t        j$                  |            dx}x}	x}x}}d   d3   }d(   d3   }||k(  }	||k(  }|	r|st        j                  d,|	|fd4|||f      t        j                  |      t        j                  |      d5t        j                         v st        j                  |      rt        j                  |      nd5d6z  }d d!|iz  }t#        t        j$                  |            dx}x}	x}}y)8u  T14b: gemini_review_gate.py CLI를 두 check name(--check-name)으로 실행했을 때
    publish_check_run에 전달되는 payload가 동일해야 함 (state/conclusion/summary).

    실제 wrapper가 동일 evaluate_gate를 호출하는지 CLI 레벨로 보장.
    r   r   r   r   r   c                >    j                  | ||||d       ddddS )N)reposhar   
conclusionsummaryr   {} rcstdoutstderr)append)r   r   r   r   r   detailscaptureds         r8   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   r   z%(py0)s == %(py3)sr   r\   r]   zPASS state should return 0 for 
>assert %(py5)sr   Nr   rY   rZ   r   r[   r^   rJ   r   r   r   r   r$   r   r   r   r   successr   r   r   r   r   r   r   r   )r   z%(py5)s == %(py6)sr1   )r   r   r   r   )r   r%   r   r&   r   r   r   r   sysmainr(   r)   rb   rc   rd   r*   r+   r,   r-   rZ   )r   r/   r0   r   r1   r%   r   
check_namer   r5   rw   @py_format4@py_format6rN   rg   r7   rP   r3   r4   r6   r   r   r   r   s                          @r8   2test_t14b_cli_two_check_names_same_publish_payloadr   <  sX    H
,,x||
$CVX./32!667
 H7 %8,GA G
C#3lNJ	*
 	 FrQwFFFrQFFFFFFrFFFrFFFQFFF9*FFFFFFFG x=A=A=A33xx=AA;v6"66"66666"6666666"66666666A;v5"55"55555"5555555"55555555A;|$NL(AN$(ANNYN(AYNNNNN$(AYNNN$NNN(ANNNYNNNNNNNA;y!;Xa[%;;!%;;;;;!%;;;;!;;;%;;;;;;;;A;vE(1+f"5E"5EEE"5EEEEE"5EEEEEE"5EEEEEEEEEEA;u?!U!3?!3??!3x?????!3x??????!3??????x???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failr   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                     r8   )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                   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(  }
|
st        j                  d|
fd||	f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |	      dz  }t        j                  d      dz   d|iz  }t!        t        j"                  |            dx}
}	d   }d}||k(  }	|	st        j                  d|	fd||f      t        j                  |      t        j                  |      dz  }t        j                  d       dz   d |iz  }t!        t        j"                  |            dx}x}	}d!}d"   }||v }	|	slt        j                  d#|	fd$||f      t        j                  |      t        j                  |      dz  }d%d |iz  }t!        t        j"                  |            dx}x}	}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   r   r   r   r   r   )r  )r   r   r   r   r   r   	publisheds         r8   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   r   r   r   r   z%HOLD should return non-zero exit coder   r   Nr   failurer   r   z0HOLD must map to 'failure', not 'neutral'. got: r   r   HOLDr   r!   r#   r$   r   )r   r%   r   r&   r   r   r   r   r   r(   r)   rb   rc   rd   r*   r+   r,   r-   )r   r/   r0   r   capsysr1   r%   r   r   r5   rw   r   r   r3   r4   r6   r7   r)  s                    @r8   )test_t16_hold_maps_to_failure_not_neutralr-    s   H
,,x||
$Cd32)>#>?@ I7 %8,GVsNH,0D	&  
			B;27;;;2;;;;;;2;;;2;;;;;;;;;;;;;\" i "i/  "i    #    '0    ;9+F     )Yy))6)))))6))))6)))))))))))r:   c                   d}| j                  |      }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	)uH   TU1-a: fenced code block (``` ... ```) 내용이 제거되는지 확인.z8Normal text
```python
BLOCKING code here
```
After blockr   not inz%(py1)s not in %(py3)sr$  r   r]   assert %(py5)sr   NzNormal textr!   z%(py1)s in %(py3)szAfter block	strip_code_blocksr(   r)   r*   rb   rc   rd   r,   r-   r/   r   r$  r3   r5   r   r   s          r8   !test_tu1_strip_code_blocks_fencedr8    s"   ID006H%:X%%%%:X%%%:%%%%%%X%%%X%%%%%%%$=H$$$$=H$$$=$$$$$$H$$$H$$$$$$$$=H$$$$=H$$$=$$$$$$H$$$H$$$$$$$r:   c                   d}| j                  |      }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>   TU1-b: inline code (` ... `) 내용이 제거되는지 확인.z Use `BLOCKING` method carefully.r   r/  r1  r$  r2  r3  r   NUser!   r4  	carefullyr5  r7  s          r8   !test_tu1_strip_code_blocks_inliner<    s"   -D006H%:X%%%%:X%%%:%%%%%%X%%%X%%%%%%%5H5H5HH";("""";(""";""""""("""("""""""r:   c                   | j                   }d} ||      }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j
                  |       rt        j                  |       ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}x}}| j                   }d} ||      }d}||u }|st        j                  d	|fd
||f      dt        j                         v st        j
                  |       rt        j                  |       ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}x}}y)u,   TU1-c: 빈 body 입력 시 그대로 반환.r   r   )zW%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.strip_code_blocks
}(%(py4)s)
} == %(py9)sr/   r\   rv   r   r   r   assert %(py11)spy11Nr_   )zW%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.strip_code_blocks
}(%(py4)s)
} is %(py9)s	r6  r(   r)   rb   rc   rd   r*   r,   r-   r/   rw   r4   rN   @py_assert8rO   r   @py_format12s           r8    test_tu1_strip_code_blocks_emptyrE    s7   ,,6R6,R06B60B66660B666666?666?666,666R6660666B6666666,,:T:,T2:d:2d::::2d::::::?:::?:::,:::T:::2:::d::::::::r:   c                   d}| j                  |      }||k(  }|st        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      nddt        j                         v st        j
                  |      rt        j                  |      nddz  }dd|iz  }t        t        j                  |            d	}y	)
uB   TU1-d: code block 없는 일반 텍스트는 변경 없이 반환.z This is a normal review comment.r   )z%(py0)s == %(py2)sr$  r   )r\   rv   zassert %(py4)sr   NrA  )r/   r   r$  rw   @py_format3r6   s         r8   "test_tu1_strip_code_blocks_no_coderH    s    -D006Ht8t88ttr:   c                   | j                  d      }d |D        }t        |      }|st        j                  d|       dz   dt	        j
                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}y)u.   TU2-a: 🔴 이모지 → emoji 패턴 매칭.u   🔴 critical issue foundc              3  $   K   | ]  }d |v  
 ywrk   rm   rn   s     r8   rq   z9test_tu2_match_high_severity_emoji_red.<locals>.<genexpr>       *w!|*rr   got: r~   rt   ru   N
match_high_severityrt   r(   r+   rb   rc   rd   r*   r,   r-   r/   r   rw   r4   r6   s        r8   &test_tu2_match_high_severity_emoji_redrP    s    ../JKD*T*:3**:*::eD6N::::::3:::3:::*:::*::::::r:   c                   | j                  d      }d |D        }t        |      }|st        j                  d|       dz   dt	        j
                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}y)u-   TU2-b: ❌ 이모지 → emoji 패턴 매칭.u   ❌ do not mergec              3  $   K   | ]  }d |v  
 ywrk   rm   rn   s     r8   rq   z7test_tu2_match_high_severity_emoji_x.<locals>.<genexpr>  rK  rr   rL  r~   rt   ru   NrM  rO  s        r8   $test_tu2_match_high_severity_emoji_xrS    s    ../ABD*T*:3**:*::eD6N::::::3:::3:::*:::*::::::r:   c                   | j                  d      }d |D        }t        |      }|st        j                  d|       dz   dt	        j
                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}y)u7   TU2-c: 'severity: critical' → severity 패턴 매칭.zseverity: critical bug herec              3  $   K   | ]  }d |v  
 ywr{   rm   rn   s     r8   rq   z@test_tu2_match_high_severity_severity_keyword.<locals>.<genexpr>  r}   rr   rL  r~   rt   ru   NrM  rO  s        r8   -test_tu2_match_high_severity_severity_keywordrV    s    ../LMD--=3--=-==tf~======3===3===-===-======r:   c                   | j                  d      }d |D        }t        |      }|st        j                  d|       dz   dt	        j
                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}y)u6   TU2-d: 'BLOCKING' 키워드 → keyword 패턴 매칭.zBLOCKING issue: auth bypassc              3  0   K   | ]  }d |v xs d|v   yw)r   r   Nrm   rn   s     r8   rq   z@test_tu2_match_high_severity_blocking_keyword.<locals>.<genexpr>  s      ?QyA~0q0?r   rL  r~   rt   ru   NrM  rO  s        r8   -test_tu2_match_high_severity_blocking_keywordrY    s    ../LMD?$?O3??O?OO5OOOOOO3OOO3OOO?OOO?OOOOOOr:   c                   | j                  d      }d |D        }t        |      }|st        j                  d|       dz   dt	        j
                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}y)u6   TU2-e: 'MUST FIX' 키워드 → keyword 패턴 매칭.zMUST FIX before mergec              3  0   K   | ]  }d |v xs d|v   yw)r   MUSTNrm   rn   s     r8   rq   z@test_tu2_match_high_severity_must_fix_keyword.<locals>.<genexpr>  s      ;yA~,1,;r   rL  r~   rt   ru   NrM  rO  s        r8   -test_tu2_match_high_severity_must_fix_keywordr]    s    ../FGD;d;K3;;K;KKuTF^KKKKKK3KKK3KKK;KKK;KKKKKKr:   c                   d}| j                  |      }d |D        }t        |      }|st        j                  d|       dz   dt	        j
                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}y)u4   TU2-f: '## High' h2 헤더 → header 패턴 매칭.z Review:

## High
- SQL injectionc              3  $   K   | ]  }d |v  
 ywr   rm   rn   s     r8   rq   z;test_tu2_match_high_severity_header_high.<locals>.<genexpr>  r   rr   rL  r~   rt   ru   NrM  r/   r   r   rw   r4   r6   s         r8   (test_tu2_match_high_severity_header_highra    s    0D..t4D+d+;3++;+;;uTF^;;;;;;3;;;3;;;+;;;+;;;;;;r:   c                   d}| j                  |      }d |D        }t        |      }|st        j                  d|       dz   dt	        j
                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}y)u8   TU2-g: '## Blocking' h2 헤더 → header 패턴 매칭.z'## Blocking
Must be fixed before merge.c              3  $   K   | ]  }d |v  
 ywr   rm   rn   s     r8   rq   z?test_tu2_match_high_severity_header_blocking.<locals>.<genexpr>  r   rr   rL  r~   rt   ru   NrM  r`  s         r8   ,test_tu2_match_high_severity_header_blockingrd    s    5D..t4D+d+;3++;+;;uTF^;;;;;;3;;;3;;;+;;;+;;;;;;r:   c                r   | 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:   TU2-h: severity 없는 일반 텍스트 → 빈 리스트.zLGTM, code looks cleanr   r   r   r   r3  r   N	rN  r(   r)   rb   rc   rd   r*   r,   r-   )r/   r   r5   rw   r   r   s         r8   %test_tu2_match_high_severity_no_matchrg    sk    ../GHD42:42442r:   c                   | j                  d      }d |D        }t        |      }| }|st        j                  d|       dz   dt	        j
                         v st        j                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}x}}y)uF   TU2-i: 'blocking' 소문자는 매칭 안됨 (정확히 대문자만).z$This might be blocking the pipeline.c              3  $   K   | ]  }d |v  
 yw)r   Nrm   rn   s     r8   rq   zKtest_tu2_match_high_severity_blocking_lowercase_no_match.<locals>.<genexpr>&  s     0a9>0rr   zunexpected keyword match: z2
>assert not %(py4)s
{%(py4)s = %(py0)s(%(py2)s)
}rt   ru   NrM  )r/   r   rw   r4   rN   r   s         r8   8test_tu2_match_high_severity_blocking_lowercase_no_matchrj  "  s    ../UVD040Us00U00U0UU4Ntf2UUUUUUUsUUUsUUU0UUU0UUUUUUr:   c                   | j                   }d} ||      }g }||k(  }|st        j                  d|fd||f      dt        j                         v st        j
                  |       rt        j                  |       ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}x}}| j                   }d} ||      }g }||k(  }|st        j                  d|fd||f      dt        j                         v st        j
                  |       rt        j                  |       ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}x}}y)	u"   TU2-j: 빈 body → 빈 리스트.r   r   )zY%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.match_high_severity
}(%(py4)s)
} == %(py9)sr/   r>  r?  r@  Nrf  rB  s           r8   "test_tu2_match_high_severity_emptyrl  )  7   ..8r8.r28b82b88882b888888?888?888.888r8882888b8888888..:t:.t4::4::::4::::::?:::?:::.:::t:::4:::::::::::r:   c                   | j                  d      }d}||v }|st        j                  d|fd||f      t        j                  |      dt	        j
                         v st        j                  |      rt        j                  |      nddz  }t        j                  d|       dz   d	|iz  }t        t        j                  |            d
x}}y
)u4   TU3-a: 'security' 단어 → security 보조 신호.zThis has security implications.r   r!   r4  sigsr2  rL  r   r   N
match_supplementaryr(   r)   r*   rb   rc   rd   r+   r,   r-   r/   ro  r3   r5   r   r   s         r8   %test_tu3_match_supplementary_securityrs  3  s}    ../PQD-:---:---:-------------tf~------r:   c                   | j                  d      }d}||v }|st        j                  d|fd||f      t        j                  |      dt	        j
                         v st        j                  |      rt        j                  |      nddz  }t        j                  d|       dz   d	|iz  }t        t        j                  |            d
x}}y
)u6   TU3-b: 'data loss' 구문 → data_loss 보조 신호.z!Risk of data loss if not handled.	data_lossr!   r4  ro  r2  rL  r   r   Nrp  rr  s         r8   &test_tu3_match_supplementary_data_lossrv  9  s}    ../RSD.;$...;$...;......$...$....%v......r:   c                   | j                  d      }d}||v }|st        j                  d|fd||f      t        j                  |      dt	        j
                         v st        j                  |      rt        j                  |      nddz  }t        j                  d|       dz   d	|iz  }t        t        j                  |            d
x}}y
)u8   TU3-c: 'regression' 단어 → regression 보조 신호.zThis could cause a regression.
regressionr!   r4  ro  r2  rL  r   r   Nrp  rr  s         r8   'test_tu3_match_supplementary_regressionry  ?  s}    ../OPD/<4///<4///<//////4///4////5//////r:   c                   d}| j                  |      }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-   TU3-d: 여러 보조 신호 동시에 존재.z>There's a security issue and risk of data loss and regression.r   r!   r4  ro  r2  r3  r   Nru  rx  )	rq  r(   r)   r*   rb   rc   rd   r,   r-   r/   r   ro  r3   r5   r   r   s          r8   %test_tu3_match_supplementary_multipler|  E  s"   KD..t4D:::;$;$;$$<4<4<44r:   c                r   | 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)	u8   TU3-e: 보조 신호 없는 텍스트 → 빈 리스트.zLGTM, everything looks fine.r   r   ro  r   r3  r   N	rq  r(   r)   rb   rc   rd   r*   r,   r-   )r/   ro  r5   rw   r   r   s         r8   %test_tu3_match_supplementary_no_matchr  N  sk    ../MND42:42442r:   c                   d}| j                  |      }d}||v}|st        j                  d|fd||f      t        j                  |      dt	        j
                         v st        j                  |      rt        j                  |      nddz  }t        j                  d|       dz   d	|iz  }t        t        j                  |            d
x}}y
)u@   TU3-f: code block 안 'security' → 보조 신호에서 제외.z(```python
# security check here
pass
```r   r/  r1  ro  r2  z2expected no security signal from code block, got: r   r   Nrp  r{  s          r8   3test_tu3_match_supplementary_in_code_block_excludedr  T  s    8D..t4D^:T!^^^:T^^^:^^^^^^T^^^T^^^^%WX\W]#^^^^^^^r:   c                   | j                   }d} ||      }g }||k(  }|st        j                  d|fd||f      dt        j                         v st        j
                  |       rt        j                  |       ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}x}}| j                   }d} ||      }g }||k(  }|st        j                  d|fd||f      dt        j                         v st        j
                  |       rt        j                  |       ndt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}x}x}}y)	u"   TU3-g: 빈 body → 빈 리스트.r   r   )zY%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.match_supplementary
}(%(py4)s)
} == %(py9)sr/   r>  r?  r@  Nr~  rB  s           r8   "test_tu3_match_supplementary_emptyr  [  rm  r:   c                   | j                  d      }d}||v }|st        j                  d|fd||f      t        j                  |      dt	        j
                         v st        j                  |      rt        j                  |      nddz  }t        j                  d|       dz   d	|iz  }t        t        j                  |            d
x}}y
)u0   TU3-h: 'Security' 대소문자 혼합도 매칭.zSecurity concern noted.r   r!   r4  ro  r2  rL  r   r   Nrp  rr  s         r8   -test_tu3_match_supplementary_case_insensitiver  a  s}    ../HID-:---:---:-------------tf~------r:   )<__doc__
__future__r   builtinsrb   _pytest.assertion.rewrite	assertionrewriter(   r   r   r   r   r  #tests.phase3_evidence_gate.conftestr   r   r	   r
   r9   rD   rR   rh   rx   r   r   r   r   r   r   r   r   r   r   r   r   r&  r-  r8  r<  rE  rH  rP  rS  rV  rY  r]  ra  rd  rg  rj  rl  rs  rv  ry  r|  r  r  r  r  rm   r:   r8   <module>r     s  
 #   
 2 2  
0"+1'.
O"U$f$Q&:$:$(2=, .
%"*()@`9@*B%#;;;>PL<<V;./0 _;.r:   