
    Pj6                       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
Z
ddlmZ ddlZ ee      j                         j                   d   Z ee      e
j&                  vr"e
j&                  j)                  d ee             ddlmZmZmZmZmZ ddZddZdd	Zdd
ZddZddZ ddZ!ddZ"ddZ#ddZ$ddZ%ddZ&ddZ'ddZ(y)u  anu_v2.tests.test_owner_trigger_pat_phase0_2553 — Phase 0 secret 인프라 단위 테스트.

회장 §명시 Phase 0 (task-2553):
  - token env name `OWNER_GEMINI_TRIGGER_PAT` 상수 정의 확인
  - token loader fail-fast (token 누락 → 즉시 예외, default GH_TOKEN fallback 금지)
  - token redaction guard (`_redact_token` 외부 출력 경로 안전성)
  - audit 박제 시 token_present + token_hash 만, token raw 0

본 회귀는 anu_v2/* 모듈만 import 한다 (one-way isolation).
    )annotationsN)Path   )OWNER_PAT_ENV_NAMEREDACTED_PLACEHOLDER_hash_token_redact_tokenload_owner_patc                 p   d} t         | k(  }|st        j                  d|fdt         | f      dt        j                         v st        j
                  t               rt        j                  t               ndt        j                  |       dz  }dd|iz  }t        t        j                  |            dx}} y)	uJ   token env 이름은 정확히 `OWNER_GEMINI_TRIGGER_PAT` 상수로 박제.OWNER_GEMINI_TRIGGER_PAT==z%(py0)s == %(py3)sr   py0py3assert %(py5)spy5N)	r   
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanation)@py_assert2@py_assert1@py_format4@py_format6s       F/home/jay/workspace/anu_v2/tests/test_owner_trigger_pat_phase0_2553.py'test_phase0_owner_pat_env_name_constantr"   #   sb    !;;!;;;;;!;;;;;;;;;;;;;!;;;;;;;;    c                   | j                  t        d       t               }d}||k(  }|st        j                  d|fd||f      dt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd|iz  }t        t        j                  |            d x}}y )Ngithub_pat_FAKE_OWNER_TOKENr   r   tokenr   r   r   )setenvr   r
   r   r   r   r   r   r   r   r   monkeypatchr&   r   r   r   r    s         r!   1test_phase0_load_owner_pat_returns_token_when_setr*   )   sx    )+HIE11511111511111115111511111111111r#   c                   | j                  t        d       t        j                  t              5 }t                ddd       j                  }t        |      }t        |v }|sCt        j                  d|fdt        |f      dt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  t              rt        j                  t              nddt        j                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      d	z  }d
d|iz  }t        t        j                  |            dx}x}}d}|j                  }t        |      }	||	v }
|
s
t        j                  d|
fd||	f      t        j                  |      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}
x}}	y# 1 sw Y   xY w)uN   token 누락 시 RuntimeError — silent fallback / default 값 반환 금지.FraisingNin)zK%(py0)s in %(py7)s
{%(py7)s = %(py2)s(%(py5)s
{%(py5)s = %(py3)s.value
})
}r   strexcinfo)r   py2r   r   py7assert %(py9)spy9OWNER_PAT_MISSING)zK%(py1)s in %(py8)s
{%(py8)s = %(py3)s(%(py6)s
{%(py6)s = %(py4)s.value
})
})py1r   py4py6py8assert %(py10)spy10)delenvr   pytestraisesRuntimeErrorr
   valuer0   r   r   r   r   r   r   r   r   )r)   r1   @py_assert4@py_assert6r   @py_format8@py_format10@py_assert0@py_assert5@py_assert7r   @py_format9@py_format11s                r!   1test_phase0_load_owner_pat_fail_fast_when_missingrK   /   s   )59	|	$  &-]]3]!33!33333!3333333333333333333333333W333W333]333!333333334gmm4#m"44"44444"4444444444#444#444444g444g444m444"44444444	 s   K""K,c                    | j                  t        d       t        j                  t              5  t                ddd       y# 1 sw Y   yxY w)uG   빈 문자열 / 공백 only token 도 fail-fast (silent 통과 금지).z   N)r'   r   r>   r?   r@   r
   r)   s    r!   6test_phase0_load_owner_pat_fail_fast_when_empty_stringrN   9   s:    )51	|	$   s   AAc                    | j                  t        d       | j                  dd       | j                  dd       t        j                  t
              5  t                ddd       y# 1 sw Y   yxY w)u   OWNER_GEMINI_TRIGGER_PAT 미설정 시, GH_TOKEN/GITHUB_TOKEN 이 환경에 있어도 fallback X.

    회장 §15 금지 14: default GH_TOKEN fallback 절대 금지.
    Fr,   GH_TOKENghp_BOT_TOKEN_LEAK_FALLBACKGITHUB_TOKENN)r=   r   r'   r>   r?   r@   r
   rM   s    r!   7test_phase0_load_owner_pat_no_default_gh_token_fallbackrS   @   sa    
 )59z#@A~'DE	|	$   s   A))A2c                   | j                  dd       t        d      }d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }dd	|iz  }t        t        j                  |            d
x}}y
)u7   token_env 인자 override 가능 (테스트 격리용).CUSTOM_TEST_PATgithub_pat_TEST_OVERRIDE)	token_envr   r   r&   r   r   r   N)
r'   r
   r   r   r   r   r   r   r   r   r(   s         r!   *test_phase0_load_owner_pat_custom_env_namerX   L   s{    (*DE%67E..5.....5.......5...5...........r#   c                    d} d|  d}t        ||       }| |v}|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 }t        |v }|st        j                  d|fdt        |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dz  }d	d
|iz  }t        t        j                  |            d }y )Ngithub_pat_VERYSECRETTOKENz'error: HTTP 401 unauthorized for token z	 (PR #81)not inz%(py0)s not in %(py2)sr&   redactedr   r2   assert %(py4)sr8   r.   )z%(py0)s in %(py2)sr   )
r	   r   r   r   r   r   r   r   r   r   )r&   textr^   r   @py_format3@py_format5s         r!   +test_phase0_redact_token_replaces_substringrd   T   s    (E4UG9EDT5)H    5      5   5                8++++8+++++++++++++++8+++8+++++++r#   c                 8   d} |  d|  d|  }t        ||       }| |v}|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 }|j                  } |t              }d}||k(  }|s
t        j                  d|fd||f      dt        j                         v st        j
                  |      rt        j                  |      ndt        j                  |      dt        j                         v st        j
                  t              rt        j                  t              ndt        j                  |      t        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            d x}x}x}}y )Nghp_AAAz ... and again z and once more r[   r]   r&   r^   r_   r`   r8      r   )zK%(py5)s
{%(py5)s = %(py2)s
{%(py2)s = %(py0)s.count
}(%(py3)s)
} == %(py8)sr   )r   r2   r   r   r:   r;   r<   )r	   r   r   r   r   r   r   r   r   countr   )r&   ra   r^   r   rb   rc   rB   rH   rC   rI   rJ   s              r!   1test_phase0_redact_token_replaces_all_occurrencesri   \   s8   EWOE7/%ADT5)H    5      5   5                >>4>./414/14444/144444484448444>444444.444.444/44414444444r#   c                    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t        j                  |       t        j                  |      t        j                  |      t        j                  |      dz  }dd	|iz  }t        t        j                  |            dx} x}x}x}}y)
u.   text=None → "" 반환 (직렬화 안전성).Nghp_X r   )z9%(py6)s
{%(py6)s = %(py0)s(%(py2)s, %(py4)s)
} == %(py9)sr	   )r   r2   r8   r9   r5   zassert %(py11)spy11	r	   r   r   r   r   r   r   r   r   )r   @py_assert3rG   @py_assert8rH   rE   @py_format12s          r!   *test_phase0_redact_token_handles_none_textrr   d   s    -w-=w'-2-'2----'2------=---=------w---'---2--------r#   c                    d} d}t        | |      }|| k(  }|s7t        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t        j                         v st        j
                  |       rt        j                  |       nddz  }dd	|iz  }t        t        j                  |            d
x}x}}y
)u   token 이 빈 문자열이면 redaction 의미 없음 → 원본 그대로 반환.

    빈 문자열로 replace 호출 시 무한 치환 등의 부작용을 방지하는 가드.
    zno token here, just plain textrl   r   )z9%(py5)s
{%(py5)s = %(py0)s(%(py1)s, %(py3)s)
} == %(py7)sr	   ra   )r   r7   r   r   r3   r4   r5   Nrn   )ra   r   rB   rC   rD   rE   s         r!   0test_phase0_redact_token_empty_token_passthroughrt   i   s    
 ,D!*=r"*"d****"d******=***=************r***"******d***d*******r#   c                    d} d| j                          d|  }t        ||       }| |v}|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}| j                   } |       }||v }|st        j                  d|fd||f      dt	        j
                         v st        j                  |       rt        j                  |       ndt        j                  |      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}x}}y)ua   fine-grained PAT 은 case-sensitive — 대소문자 다르면 매칭 안 됨 (의도된 동작).
ghp_AbCdEfzupper z mixed r[   r]   r&   r^   r_   r`   r8   Nr.   )zD%(py4)s
{%(py4)s = %(py2)s
{%(py2)s = %(py0)s.upper
}()
} in %(py6)s)r   r2   r8   r9   assert %(py8)sr:   )
upperr	   r   r   r   r   r   r   r   r   )
r&   ra   r^   r   rb   rc   ro   rG   @py_format7rI   s
             r!   'test_phase0_redact_token_case_sensitiverz   r   s!   EEKKM?'%1DT5)H    5      5   5                ;;$;=$=H$$$$=H$$$$$$5$$$5$$$;$$$=$$$$$$H$$$H$$$$$$$r#   c                    d} t        |       }t        |      }d}||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  t              rt        j                  t              nddt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      t        j                  |      dz  }dd	|iz  }t        t        j                  |            d x}x}}t        j                  | j                  d
            j                         }|d d }||k(  }|st        j                  d|fd||f      dt	        j
                         v st        j                  |      rt        j                  |      ndt        j                  |      dz  }	dd|	iz  }
t        t        j                  |
            d x}}| |v}|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 )Nr%      r   )z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)slenh)r   r7   r   r9   rw   r:   zutf-8r   r   r   r   r[   r]   r&   r_   r`   r8   )r   r}   r   r   r   r   r   r   r   r   hashlibsha256encode	hexdigest)r&   r~   r   rG   rB   ry   rI   expected_fullr   r   r    rb   rc   s                r!   1test_phase0_hash_token_returns_12_char_hex_prefixr   }   s   )EEAq6R6R<6R33qq6RNN5<<#89CCEMcr""1"""""1"""""""1"""1""""""""""">555r#   c                    t        d      } t        d      }| |k7  }|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 )
Nrf   ghp_BBB)!=)z%(py0)s != %(py2)sh1h2r_   r`   r8   	r   r   r   r   r   r   r   r   r   )r   r   r   rb   rc   s        r!   8test_phase0_hash_token_different_tokens_different_hashesr      sa    	Y	B	Y	B8OOO2OOOOOO2OOO2OOOOOOOOOOOOOOOOr#   c            	     P   d} t        |       }t        |       }||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                  |      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                  |      dz  }dd|iz  }t        t        j                  |            d	x}x}}y	)
uB   동일 token 은 동일 hash — dedupe / 비교에 사용 가능.github_pat_REPEATr   )zN%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py8)s
{%(py8)s = %(py5)s(%(py6)s)
}r   r&   )r   r7   r   r   r9   r:   r;   r<   Nr   )r&   r   rH   rB   rI   rJ   s         r!   $test_phase0_hash_token_deterministicr      s    Eu3U!33!33333!3333333;333;333333u333u333333333333333333U333U333!33333333r#   )returnNone)r)   zpytest.MonkeyPatchr   r   ))__doc__
__future__r   builtinsr   _pytest.assertion.rewrite	assertionrewriter   r   syspathlibr   r>   __file__resolveparentsWORKSPACE_ROOTr0   pathinsertanu_v2.owner_trigger_patr   r   r   r	   r
   r"   r*   rK   rN   rS   rX   rd   ri   rr   rt   rz   r   r   r    r#   r!   <module>r      s   	 #    
   h'')11!4~chh&HHOOAs>*+ <25	/,5.
+%4r#   