
    9j                    H   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 ddlZ ee      j                         j                  d   dz  Zedz  ZdZd	Zd
ZdZdZdZddZddZ	 	 	 	 	 	 ddZd Zd Zd Zd Zd Zd Z ejB                  jE                  dg d      d        Z#y)u9  anu_v2.tests.test_owner_trigger_403_token_scope_mismatch_classification — task-2557 §Test-1.

회장 §명시 2026-05-12 Track C Rank 1 1:1 박제:
  PR #107 attempt-4 raw 403 evidence (``owner_trigger_403_dual_scope_missing.json``)
  fixture 1 건을 로드해 4 가지 회귀를 영구 catch.

회귀 catch 4 종:
  1. evidence schema = ``anu_v2.owner_trigger_evidence.owner-trigger-failed.v2``
  2. ``response_headers["x-accepted-github-permissions"]`` 파싱 (issues=write; pull_requests=write)
  3. ``missing_permission`` diff 자동 계산 = ``pull_requests=write`` (1 건)
  4. classification = ``TOKEN_SCOPE_MISMATCH_DIAGNOSED`` (Critical 7 #6)

본 test 는 fixture 기반 evidence 회귀만 수행 (owner_trigger_only / executor_scheduler 모듈 코드
변경 0 — 회장 §명시 task-2557 금지 12 #12 1:1).
    )annotationsN)Path   fixturesz)owner_trigger_403_dual_scope_missing.jsonz5anu_v2.owner_trigger_evidence.owner-trigger-failed.v2TOKEN_SCOPE_MISMATCH_DIAGNOSEDpull_requests=writez6/repos/Jeon-Jonghyuk/dev_workspace/issues/107/commentsa9e05574(e03f536ad7ad626f1a2afca080d1952588f53a71c                 T    t        j                  t        j                  d            S )Nzutf-8)encoding)jsonloadsFIXTURE_PATH	read_text     ^/home/jay/workspace/anu_v2/tests/test_owner_trigger_403_token_scope_mismatch_classification.py_load_fixturer   '   s    ::l,,g,>??r   c                    t        | t              r| sg S | j                  d      D cg c]#  }|j                         s|j                         % c}S c c}w )u   ``X-Accepted-GitHub-Permissions`` 헤더 파싱.

    GitHub 응답에서 본 헤더는 ``issues=write; pull_requests=write`` 형식.
    세미콜론 구분, 각 토큰은 ``<resource>=<level>``.
    ;)
isinstancestrsplitstrip)header_valuetoks     r   _parse_accepted_permissionsr   +   sB     lC(	#/#5#5c#:JCciikCIIKJJJs
   AAc                L    t        |      }| D cg c]	  }||vs| c}S c c}w )uC   granted 에 없는 required 권한만 반환. 입력 순서 보존.)set)requiredgrantedgranted_setperms       r    _compute_missing_permission_diffr$   6   s(     g,K%AT[)@DAAAs   	!!c                    t               } | d   }|t        k(  }|st        j                  d|fd|t        f      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }dd|iz  }t        t        j                  |            d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}}| 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}}| d   }|t        k(  }|st        j                  d|fd|t        f      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }dd|iz  }t        t        j                  |            dx}}| d   }|t        k(  }|st        j                  d|fd|t        f      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }dd|iz  }t        t        j                  |            d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}}| 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}}| 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}}| d   }|t        k(  }|st        j                  d|fd|t        f      t        j                  |      d t        j                         v st        j                  t              rt        j                  t              nd dz  }dd|iz  }t        t        j                  |            dx}}| 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                  |	      d$z  }d%d&|iz  }t        t        j                  |            dx}x}x}
}	y)'u?   fixture 1 의 schema 가 owner-trigger-failed.v2 임을 박제.schema==z%(py1)s == %(py3)sEXPECTED_SCHEMApy1py3assert %(py5)spy5Nhttp_status  z%(py1)s == %(py4)sr,   py4assert %(py6)spy6prk   attempt   headEXPECTED_HEADendpointEXPECTED_ENDPOINTmethodPOSTtoken_value_loggedF)is)z%(py1)s is %(py4)stoken_presentTtoken_hash_prefixEXPECTED_TOKEN_HASH_PREFIX   )z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py7)slen)py0py2r4   py7zassert %(py9)spy9)r   r*   
@pytest_ar_call_reprcompare	_saferepr@py_builtinslocals_should_repr_global_nameAssertionError_format_explanationr<   r>   rE   rG   )fx@py_assert0@py_assert2@py_format4@py_format6@py_assert3@py_format5@py_format7@py_assert1@py_assert6@py_assert5@py_format8@py_format10s                r   =test_fixture_loads_and_schema_matches_owner_trigger_failed_v2ra   A   s   	Bh<*<?****<?***<******?***?*******m###################d8s8s?8s8si=A=A=A=Af:&:&&&&:&&&:&&&&&&&&&&&&&&&&j>.>.....>....>..................h<!6!<6!!!!<6!!!<!!!6!!!!!!!"#,u,#u,,,,#u,,,#,,,u,,,,,,,o&$&$&&&&$&&&&&&$&&&&&&&!"@"&@@@@@"&@@@@"@@@@@@&@@@@&@@@@@@@@%&,3&',1,'1,,,,'1,,,,,,3,,,3,,,&,,,',,,1,,,,,,,r   c                    t               } | d   }t        |t              }|s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dt        j                         v st        j                  t              rt        j                  t              ndt        j                  |      dz  }t        t        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}}t        |      }	d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}}| d   }
|
|k(  }|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)uf   response_headers 에 x-accepted-github-permissions 가 dual scope (issues + pull_requests) 로 박제.response_headersz5assert %(py4)s
{%(py4)s = %(py0)s(%(py1)s, %(py2)s)
}r   headersdict)rH   r,   rI   r4   Nzx-accepted-github-permissionsz!issues=write; pull_requests=writer'   z%(py0)s == %(py3)srawrH   r-   r.   r/   issues=writer   parsedx_accepted_github_permissionsr)   r+   )r   r   re   rO   rP   rL   rQ   rN   rR   rS   getrM   r   )rT   rd   rY   rZ   rg   rV   r\   rW   rX   rj   rU   s              r   Etest_response_headers_x_accepted_github_permissions_parses_dual_scoperm   R   s   	B#$Ggt$$$$$$$$:$$$:$$$$$$g$$$g$$$$$$t$$$t$$$$$$$$$$
++5
6C55355555355555553555355555555555(-F$&;<<6<<<<<6<<<<<<<6<<<6<<<<<<<<<<<-.5.#5555.#555.555555#555#5555555r   c                    t               } | d   }| 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  }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}}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}}t        ||      }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}}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   }|t        k(  }|st        j                  d|fd|t        f      t        j                  |      dt	        j
                         v st        j                  t              rt        j                  t              nddz  }dd	|iz  }t        t        j                  |            d
x}}| d   }||k(  }|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
)ud   missing_permission diff 자동 계산 — issues=write 는 granted, pull_requests=write 만 missing.permissions_required_set!permissions_currently_granted_setri   inz%(py1)s in %(py3)sr!   r+   r.   r/   Nr   )not in)z%(py1)s not in %(py3)sr    r'   rf   diffrh   r   )z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)srG   rH   r,   r-   r6   assert %(py8)spy8missing_permissionr)   EXPECTED_MISSING_PERMISSIONmissing_permissions_list)r   rL   rM   rN   rO   rP   rQ   rR   rS   r$   rG   rz   )rT   r    r!   rU   rV   rW   rX   ru   r\   r^   @py_assert4r[   @py_format9s                r   Ctest_missing_permission_diff_auto_computed_pull_requests_write_onlyr~   `   sG   	B,-H45G$>W$$$$>W$$$>$$$$$$W$$$W$$$$$$$ / //// /// ////////////////%>X%%%%>X%%%>%%%%%%X%%%X%%%%%%% , H,,,, H,,, ,,,,,,H,,,H,,,,,,,+Hg>D)**4*****4*******4***4***********t99>933tt9"#B#'BBBBB#'BBBB#BBBBBB'BBBB'BBBBBBBB()1)T1111)T111)111111T111T1111111r   c                    t               } | d   }|t        k(  }|st        j                  d|fd|t        f      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }dd|iz  }t        t        j                  |            dx}}| d	   }|t        k(  }|st        j                  d|fd|t        f      t        j                  |      dt        j                         v st        j                  t              rt        j                  t              nddz  }dd|iz  }t        t        j                  |            d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}}| j                  }d} ||      }	|	sddt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      t        j                  |	      dz  }t        t        j                  |            dx}x}}	t        | 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   }||v }|slt        j                  d |fd!||f      t        j                  |      t        j                  |      dz  }dd|iz  }t        t        j                  |            dx}x}}y)"uG   classification = TOKEN_SCOPE_MISMATCH_DIAGNOSED (Critical 7 #6) 박제.classificationr'   r)   EXPECTED_CLASSIFICATIONr+   r.   r/   Ncritical_seven_coder0   r1   r2   r3   r5   r6   rk   zEassert %(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get
}(%(py4)s)
}rT   )rH   rI   r4   r6   ro   rp   r   )>=)z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} >= %(py6)srG   missingrv   rw   rx   z0Resource not accessible by personal access tokenhttp_response_messagerq   )z%(py1)s in %(py4)s)r   r   rL   rM   rN   rO   rP   rQ   rR   rS   rl   r$   rG   )rT   rU   rV   rW   rX   rY   rZ   r[   r\   r^   r   r|   r}   s                r   Ctest_classification_token_scope_mismatch_diagnosed_critical_seven_6r   r   s   	B:#:::::#::::::::::#::::#::::::::#$?$(?????$(????$??????(????(????????m###################66212612222222222222222622212222222222.
%&
./G w<1<1<133ww<1=\D[A\\=A\\\\\=A\\\\=\\\A\\\\\\\\r   c                 J   t               } | d   }|j                  }d} ||      }|stdt        j                  |      t        j                  |      t        j                  |      t        j                  |      dz  }t	        t        j
                  |            dx}x}x}}y)uj   documentation_url 박제 — GitHub 응답 본체 그대로 보존 (회귀 시 endpoint URL 변경 catch).documentation_urlz,https://docs.github.com/rest/issues/commentszLassert %(py7)s
{%(py7)s = %(py3)s
{%(py3)s = %(py1)s.startswith
}(%(py5)s)
})r,   r-   r/   rJ   N)r   
startswithrL   rN   rR   rS   )rT   rU   rV   r|   r]   r_   s         r   ;test_documentation_url_points_to_github_rest_issue_commentsr      su    	B!"]"--].\]-.\]]]]]"]]]-]]].\]]]]]]]]]]r   c                     t               } | 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  }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	)uU   expected_resolution 박제 — token regenerate 불필요, owner UI 권한 추가만.expected_resolutionzPull requestsrq   rs   
resolutionr+   r.   r/   NzRead and writeu   token regenerate 불필요)	r   rL   rM   rN   rO   rP   rQ   rR   rS   )rT   r   rU   rV   rW   rX   s         r   ?test_expected_resolution_describes_owner_ui_permission_add_onlyr      s%   	B)*J(?j((((?j(((?((((((j(((j((((((()z))))z)))))))))z)))z)))))))'5':5555':555'555555:555:5555555r   field)r&   task_idr9   r7   r;   tsr=   r?   r0   r   rc   r   rk   currently_set_per_owner_uirC   rD   rA   ry   r{   ro   rp   r   r   comment_body_attemptedr   lesson_pinnedc                   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  }t        j                  d|        dz   d|iz  }t        t        j                  |            d	}y	)
uA   schema owner-trigger-failed.v2 필수 필드 누락 회귀 catch.rq   )z%(py0)s in %(py2)sr   rT   )rH   rI   z;required field missing in owner-trigger-failed.v2 fixture: z
>assert %(py4)sr4   N)
r   rL   rM   rO   rP   rQ   rN   _format_assertmsgrR   rS   )r   rT   r\   @py_format3rZ   s        r   4test_owner_trigger_failed_v2_required_fields_presentr      s    B 
BB;]]]5B]]]]]]5]]]5]]]]]]B]]]B]]]]UV[U\]]]]]]]r   )returnre   )r   r   r   	list[str])r    r   r!   r   r   r   )$__doc__
__future__r   builtinsrO   _pytest.assertion.rewrite	assertionrewriterL   r   pathlibr   pytest__file__resolveparentsFIXTURES_DIRr   r*   r   rz   r>   rE   r<   r   r   r$   ra   rm   r~   r   r   r   markparametrizer   r   r   r   <module>r      s     #      H~%%'//2Z?III: 3 L ' :@KBB"+BB-"62$]"^6 >^?>^r   