
    nair2                       d Z ddlmZ ddl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j                  j                  d ee             edz  dz  Zej$                  j'                  de      Zeej*                   e	j,                  e d	d
       ej$                  j/                  e      Zej*                  j3                  e       d Zd Zd Zd Zd Zd Zy)u   tests/regression/test_refresh_bot_token.py — task-2483 회귀 테스트.

모리건(개발3팀 테스터) 작성.
scripts/refresh_bot_token.py 인터페이스 명세 기반 6항목.
루(Lugh)의 구현이 없으면 전체 SKIP (ImportError guard).
    )annotationsN)Path   scriptszrefresh_bot_token.pyrefresh_bot_tokenu&    미작성 — 루(Lugh) 구현 대기T)allow_module_levelc                    ddl m}  ddlm} |j	                  dd      }|j                  | j                  j                  | j                  j                  | j                               }d}t        j                  d	||
      }ddl}|j                  |      }|j                  |ddi      }|d   dk(  sJ |d   d	k(  sJ |d   |dz
  k(  sJ |d   |dz   k(  sJ y)uU   JWT는 RS256으로 발급되며, iat=now-60 (시계 skew 흡수), exp=now+540 (9분).r   serializationrsa     )public_exponentkey_size)encodingformatencryption_algorithmi Se3616524)nowNverify_signatureF)optionsalgRS256issiat<   expi  )cryptography.hazmat.primitivesr   )cryptography.hazmat.primitives.asymmetricr   generate_private_keyprivate_bytesEncodingPEMPrivateFormatPKCS8NoEncryptionrbtgenerate_jwtjwtget_unverified_headerdecode)	r   r   private_key	pem_bytes	fixed_nowtokenpyjwtheaderpayloads	            X/home/jay/workspace/.worktrees/task-2516-dev3/tests/regression/test_refresh_bot_token.py#test_jwt_generation_rs256_with_skewr5      s    <=**54*PK))''++**00*779 * I IY	yAE((/Fll5+=u*ElFG%=G###5>Y&&&5>Y^+++5>Y_,,,    c                   | dz  }|j                  d       t        j                  t        |      | dz        }||k(  sJ d       |j	                         sJ | dz  }|j                  d       t        j                  t        | dz        |      }|j	                         sJ d	       | d
z  }|j                  d       |j                          | dz  }t        d      }|j	                         sCt        j                  t              5  t        j                  t        |      |       ddd       yt        j                  t        |      |      }|j	                         sJ d       y# 1 sw Y   yxY w)u  PEM 경로 우선순위 및 fallback 동작 검증.

    인터페이스 명세: env_path 존재 → 그 경로 반환. env_path None/미존재 → fallback.
    둘 다 없으면 FileNotFoundError.

    구현 align 주석: resolve_pem_path() 내부에 하드코딩된
    /home/jay/.secrets/... 경로가 candidates 중간에 삽입된다.
    이 파일이 실서버에서 실제 존재하므로, env_path=None 시 항상 존재하는 경로가 반환된다.
    FileNotFoundError case는 CI(실서버 .secrets 없음) 환경에서만 재현 가능.
    → 환경 독립적으로 검증 가능한 case만 PASS 기준으로 설정.
    zprimary.pemprimaryzfallback.pem)fallback_pathu(   env_path 존재 시 해당 경로 반환
backup.pemdummyzmissing.pemu%   후보 중 존재하는 경로 반환z	ghost.pemxznone_fallback.pemzG/home/jay/.secrets/jeon-jonghyuk-taskctl-bot.2026-05-05.private-key.pemNu5   hardcoded fallback 경로 반환 시 존재해야 함)

write_textr(   resolve_pem_pathstrexistsunlinkr   pytestraisesFileNotFoundError)	tmp_pathr8   resultfallbackreturnedghostnone_fallback	hardcodedreturned_hardcodeds	            r4   #test_pem_fallback_when_main_missingrM   <   sl    &Gy!!!#g,h>W!XFWHHH ==?? ,&H ##C=(@$AQY#ZH??EEE {"E	S	LLN22M ^_I]],- 	J  U= I	J 	J !11#e*M1Z!((*c,cc*	J 	Js   !E))E2c                   | dz  }d}|j                  d| d       | dz  }ddlm} ddlm} |j                  d	d
      j                  |j                  j                  |j                  j                  |j                               }| dz  }|j                  |       d }	|j                  t        d|	       |j                  t        d|       t        j                  dt!        |      dt!        |      g      }
|
dk(  sJ d       ||j#                         v sJ |j#                         j%                         D cg c](  }|j'                         st)        j*                  |      * }}t-        d |D              sJ yc c}w )uI   API 401 → main() exit 1 + .env.keys 미수정 + audit refresh_rejected.	.env.keysghs_OLDoldoldoldoldOLDOLDzyBOT_GITHUB_APP_ID=3616524
BOT_GITHUB_INSTALLATION_ID=129882070
BOT_GITHUB_PRIVATE_KEY_PATH=/nonexistent
BOT_GITHUB_TOKEN=
audit.jsonlr   r
   r   r   r   r:   c                    t        dd      )Ni  zBad credentials)RuntimeError
_jwt_token_installation_id_kwargss      r4   fake_requestzBtest_api_401_fail_closed_preserves_old_token.<locals>.fake_request   s    3 122r6   request_installation_tokenDEFAULT_PEM_BACKUP
--env-keys--audit-path   u   fail-closed 시 exit 1c              3  *   K   | ]  }|d    dv   yw)status)refresh_rejected	api_errorN ).0lines     r4   	<genexpr>z?test_api_401_fail_closed_preserves_old_token.<locals>.<genexpr>   s     [ttH~!BB[s   N)r=   r   r   r    r   r!   r"   r#   r$   r%   r&   r'   write_bytessetattrr(   mainr?   	read_text
splitlinesstripjsonloadsany)rE   monkeypatchenv_keys	old_token
audit_pathr   r   pempem_pathrY   rclaudit_liness                r4   ,test_api_401_fail_closed_preserves_old_tokenry   q   su   +%H+I &;b	* M)J<=

"
"5$
/
=
=""##))""$C
 ,&H3 9<H18<	c(mJ 
B
 7,,,7**,,,,*4*>*>*@*K*K*M[QQRQXQXQZ4::a=[K[[{[[[[ \s   9E=E=c                   | dz  }t         j                  |ddd       t         j                  |dddd	       t         j                  |d
dd       |j                         j                         D cg c](  }|j	                         st        j                  |      * }}t        |      dk(  sJ |d   d   dk(  sJ |d   d   dk(  sJ |d   d   d
k(  sJ yc c}w )u0   append_audit 호출 N번 → N개 라인 누적.rR   	refreshedabc12345z2026-05-07T23:00:00Z)r`   sha256_prefix
expires_atra   NzHTTP 401)r`   r}   r~   errordry_rundef67890z2026-05-07T23:30:00Z   r   r`   r^   r   )r(   append_auditrj   rk   rl   rm   rn   len)rE   rs   rw   liness       r4   test_audit_append_onlyr      s    M)JZ:ZpqZ(:$[_gqrZ	Xno$.$8$8$:$E$E$GUq1779TZZ]UEUu:??8H,,,8H!33338H***	 Vs   2CCc                   | dz  }|j                  d       | dz  }ddlm} ddlm} |j                  dd      j                  |j                  j                  |j                  j                  |j                               }| d	z  }|j                  |       d
fd}	|j                  t        d|	       |j                  t        d|       t        j                  dt!        |      dt!        |      g      }
|
dk(  sJ |j#                         }|j$                  |j&                  z   }|vsJ d       |j)                         vsJ d       |j)                         v sJ y)uM   성공 시 stdout/audit 어디에도 토큰 원문이 등장하지 않는다.rO   zBOT_GITHUB_APP_ID=3616524
BOT_GITHUB_INSTALLATION_ID=129882070
BOT_GITHUB_PRIVATE_KEY_PATH=/nonexistent
BOT_GITHUB_TOKEN=ghs_OLDOLDOLD
rR   r   r
   r   r   r   r:   'ghs_VERYSECRETtokenABCDEFGHIJ0123456789c                    ddS )N2026-05-08T00:00:00Zr0   r~   rc   )rV   rW   rX   secret_tokens      r4   rY   z7test_token_never_logged_plaintext.<locals>.fake_request   s    %5KLLr6   rZ   r[   r\   r]   u,   stdout/stderr에 토큰 원문 노출 금지u*   audit jsonl에 토큰 원문 기록 금지N)r=   r   r   r    r   r!   r"   r#   r$   r%   r&   r'   rg   rh   r(   ri   r?   
readouterrouterrrj   )rE   capsysrp   rq   rs   r   r   rt   ru   rY   rv   capturedr   r   s                @r4   !test_token_never_logged_plaintextr      s`   +%H	+ M)J<=

"
"5$
/
=
=""##))""$C
 ,&H<LM 9<H18<	c(mJ 
B 7N7  "H
,,
%Cs"R$RR"z3355c7cc58--////r6   c           	     T   | dz  }|j                  d       | dz  }ddlm} ddlm} |j                  dd      j                  |j                  j                  |j                  j                  |j                               }| d	z  }|j                  |       d
 }|j                  t        d|       |j                  t        d|       t        j                  ddt!        |      dt!        |      g      dk(  sJ t        j                  dt!        |      dt!        |      g      dk(  sJ y)uJ   성공 케이스에서 main()은 0 반환 → systemd Type=oneshot 정상.rO   zBOT_GITHUB_APP_ID=3616524
BOT_GITHUB_INSTALLATION_ID=129882070
BOT_GITHUB_PRIVATE_KEY_PATH=/nonexistent
BOT_GITHUB_TOKEN=ghs_OLDOLD
rR   r   r
   r   r   r   r:   c                    dddS )N
ghs_NEWNEWr   r   rc   rU   s      r4   rY   z?test_systemd_oneshot_compatible_exit_zero.<locals>.fake_request   s    %5KLLr6   rZ   r[   z	--dry-runr\   r]   N)r=   r   r   r    r   r!   r"   r#   r$   r%   r&   r'   rg   rh   r(   ri   r?   )	rE   rp   rq   rs   r   r   rt   ru   rY   s	            r4   )test_systemd_oneshot_compatible_exit_zeror      s   +%H	( M)J<=

"
"5$
/
=
=""##))""$C
 ,&HM 9<H18< 88[,H~sS]_`deeee88\3x=.#j/RSWXXXXr6   ) __doc__
__future__r   importlib.util	importlibrm   syspathlibr   rB   __file__resolveparents_WORKSPACE_ROOTpathinsertr?   	_RBT_PATHutilspec_from_file_location_specloaderskipmodule_from_specr(   exec_moduler5   rM   ry   r   r   r   rc   r6   r4   <module>r      s    #   
  x.((*2215 3' (i'*@@	../BIN=ELL(FKK9+CDY]^nn%%e,    -:.dj'\\+$(0^Yr6   