
    UAj0%                       S r SSKJr  SSKrSSKJs  Jr  SSK	r	SSK
r
SSKrSSKJr  \R                  R                  S\" \" \5      R%                  5       R&                  S   S-  5      5         " S S\	R(                  5      rSSS	 jjrS
 rS rS rS rS r " S S5      rS rS rS r " S S5      r \!S:X  a  SSK"r"SSK#r#Sr$\%" S \&" \'" 5       5      RQ                  5        5       5       H  u  r)r*\ " 5       r+\*RX                  RZ                  S\*RX                  R\                   r/0 r0S\/;   a  \+\0S'   S\/;   a  \" \"Rb                  " 5       5      \0S'    \*" S0 \0D6  \2" S\) 35        \+Rk                  5         M     \Rl                  " \$(       a  SOS5        gg! \3 a$    \$S-  r$\2" S\) 35        \#Rh                  " 5          NZf = f! \+Rk                  5         f = f)ap  Phase 1 contract-alignment tests for the WolfPanel Agent API client.

Verifies the frozen v1 contract on the agent side:
  * paths under /api/v1/agent/*
  * X-Agent-Token header on agent-authenticated calls (never Bearer)
  * register response handling = {agent_token, server_id, status}

These tests capture the outgoing urllib Request without doing any network I/O.
    )annotationsN)Path   srcc                       \ rS rSrS rS rSrg)_FakeResponse   c                    U $ N selfs    9D:\Programs\laragon\www\wolf\agent\tests\test_contract.py	__enter___FakeResponse.__enter__   s        c                    g)NFr   )r   excs     r   __exit___FakeResponse.__exit__   s    r   r   N)__name__
__module____qualname____firstlineno__r   r   __static_attributes__r   r   r   r   r      s    r   r   c                v   ^^ SSK n/ mSUU4S jjnU R                  UR                  R                  SU5        T$ )z;Patch urllib so RealApiClient calls are captured, not sent.r   Nc                   > TR                  U 5        [        [        R                  " T=(       d    0 5      R	                  S5      5      $ )Nzutf-8)appendr   jsondumpsencode)requesttimeoutbodycaptureds     r   fake_urlopen'_capture_requests.<locals>.fake_urlopen#   s2     TZZ
3::7CDDr   urlopenr   )
api_clientsetattrurllibr"   )monkeypatchr$   r)   r&   r%   s    `  @r   _capture_requestsr-      s<    HE E 
))119lKOr   c                   SS K n[        U SSSS.5      nUR                  S5      nUR                  SSS	.5      nUS   nUR                  nS
ovU:H  o(       d  [
        R                  " SU4SXg45      S[        R                  " 5       ;   d  [
        R                  " U5      (       a  [
        R                  " U5      OS[
        R                  " U5      [
        R                  " U5      S.-  n	SSU	0-  n
[        [
        R                  " U
5      5      eS =n=pUR                  nSo" U5      nS oUL o(       d  [
        R                  " SU4SX45      S[        R                  " 5       ;   d  [
        R                  " U5      (       a  [
        R                  " U5      OS[
        R                  " U5      [
        R                  " U5      [
        R                  " U5      [
        R                  " U5      S.-  nSSU0-  n[        [
        R                  " U5      5      eS =n=n=n=pUR                  nSo" U5      nS oUL o(       d  [
        R                  " SU4SX45      S[        R                  " 5       ;   d  [
        R                  " U5      (       a  [
        R                  " U5      OS[
        R                  " U5      [
        R                  " U5      [
        R                  " U5      [
        R                  " U5      S.-  nSSU0-  n[        [
        R                  " U5      5      eS =n=n=n=pSSSS.nUU:H  of(       d  [
        R                  " SU4SUU45      S[        R                  " 5       ;   d  [
        R                  " U5      (       a  [
        R                  " U5      OS[
        R                  " U5      S.-  nSSU0-  n	[        [
        R                  " U	5      5      eS =nng )Nr   tr   active)agent_token	server_idstatushttps://api.example.comx1)install_tokenagent_versionz-https://api.example.com/api/v1/agent/register==z0%(py2)s
{%(py2)s = %(py0)s.full_url
} == %(py5)sreqpy0py2py5assert %(py7)spy7X-agent-tokeniszP%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get_header
}(%(py4)s)
} is %(py9)sr>   r?   py4py6py9assert %(py11)spy11Authorizationz%(py0)s == %(py3)srespr>   py3assert %(py5)sr@   )r)   r-   RealApiClientregisterfull_url
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprAssertionError_format_explanation
get_header)r,   r)   r%   clientrO   r<   @py_assert1@py_assert4@py_assert3@py_format6@py_format8@py_assert5@py_assert8@py_assert7@py_format10@py_format12@py_assert2@py_format4s                     r   0test_register_targets_v1_path_and_no_auth_headerrl   +   sQ    SqHMH %%&?@F??S3GHD
1+C<<JJJJJJJJJ<JJJJJJ3JJJJ3JJJ<JJJJJJJJJJJ>>2/2>/*2d2d22222*222222322223222>222/222*222d2222222>>2/2>/*2d2d22222*222222322223222>222/222*222d2222222#&Q(KK4KKKKKK4KKKKKKK4KKKK4KKKKKKKKKKKr   c                X   SS K n[        U 5      nUR                  S5      nUR                  SSS.SS9  US   nUR                  nSoeU:H  ow(       d  [
        R                  " S	U4S
XV45      S[        R                  " 5       ;   d  [
        R                  " U5      (       a  [
        R                  " U5      OS[
        R                  " U5      [
        R                  " U5      S.-  nSSU0-  n	[        [
        R                  " U	5      5      eS =n=pvUR                  nSou" U5      n
SoU:H  o(       d  [
        R                  " S	U4SX45      S[        R                  " 5       ;   d  [
        R                  " U5      (       a  [
        R                  " U5      OS[
        R                  " U5      [
        R                  " U5      [
        R                  " U
5      [
        R                  " U5      S.-  nSSU0-  n[        [
        R                  " U5      5      eS =n=n=n
=pUR                  nSou" U5      n
S oUL o(       d  [
        R                  " SU4SX45      S[        R                  " 5       ;   d  [
        R                  " U5      (       a  [
        R                  " U5      OS[
        R                  " U5      [
        R                  " U5      [
        R                  " U
5      [
        R                  " U5      S.-  nSSU0-  n[        [
        R                  " U5      5      eS =n=n=n
=pg )Nr   r4   r6   online)r8   r3   secret-agent-tokentokenz.https://api.example.com/api/v1/agent/heartbeatr9   r;   r<   r=   rA   rB   rC   )zP%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get_header
}(%(py4)s)
} == %(py9)srG   rK   rL   rM   rD   rF   )r)   r-   rS   	heartbeatrU   rV   rW   rX   rY   rZ   r[   r\   r]   r^   )r,   r)   r%   r_   r<   r`   ra   rb   rc   rd   re   rf   rg   rh   ri   s                  r   (test_heartbeat_uses_x_agent_token_headerrs   =   s    -H%%&?@F
sh?G[\
1+C<<KKKKKKKKK<KKKKKK3KKKK3KKK<KKKKKKKKKKK>>B/B>/*B.BB.BBBBBB*BBBBBB3BBBB3BBB>BBB/BBB*BBB.BBBBBBBB>>2/2>/*2d2d22222*222222322223222>222/222*222d22222222r   c            	        SSK Jn   U " 5       R                  SS05      nUR                  o"" 5       n[	        U5      n1 SkoTU:H  of(       Gd7  [
        R                  " SU4SXE45      S[        R                  " 5       ;   d  [
        R                  " [        5      (       a  [
        R                  " [        5      OSS	[        R                  " 5       ;   d  [
        R                  " U5      (       a  [
        R                  " U5      OS	[
        R                  " U5      [
        R                  " U5      [
        R                  " U5      [
        R                  " U5      S
.-  nSSU0-  n[        [
        R                  " U5      5      eS =n=n=n=peSoU;  o"(       d  [
        R                  " SU4SX45      [
        R                  " U	5      S	[        R                  " 5       ;   d  [
        R                  " U5      (       a  [
        R                  " U5      OS	S.-  n
SSU
0-  n[        [
        R                  " U5      5      eS =pSoU;  o"(       d  [
        R                  " SU4SX45      [
        R                  " U	5      S	[        R                  " 5       ;   d  [
        R                  " U5      (       a  [
        R                  " U5      OS	S.-  n
SSU
0-  n[        [
        R                  " U5      5      eS =pg )Nr   )MockApiClienthostnameh>   r3   r2   r1   r9   )zb%(py7)s
{%(py7)s = %(py0)s(%(py5)s
{%(py5)s = %(py3)s
{%(py3)s = %(py1)s.keys
}()
})
} == %(py10)ssetrO   )r>   py1rQ   r@   rB   py10zassert %(py12)spy12agent_idnot inz%(py1)s not in %(py3)sry   rQ   rR   r@   refresh_token)r)   ru   rT   keysrx   rV   rW   rX   rY   rZ   r[   r\   r]   )ru   rO   rj   ra   @py_assert6@py_assert9rf   @py_format11@py_format13@py_assert0rk   rc   s               r   !test_mock_register_response_shaper   K   s   (?##Z$56DyyEy{E3{EEEEEEEEEEEEEEE3EEEE3EEEEEEtEEEEtEEEyEEE{EEEEEEEEEEEEEE!T!!!!!:!!!:!!!!!!T!!!!T!!!!!!!&$&&&&&?&&&?&&&&&&$&&&&$&&&&&&&r   c                *   UR                  S[        U 5      5        UR                  SS5        SS KnSS KnUR	                  U5        UR                  5       nSSKJnJnJ	n  U" U5        U" USS5        U" U5      nSoU	:H  o(       Gd  [        R                  " SU
4S	X45      S
[        R                  " 5       ;   d  [        R                  " U5      (       a  [        R                  " U5      OS
S[        R                  " 5       ;   d  [        R                  " U5      (       a  [        R                  " U5      OS[        R                  " U5      [        R                  " U	5      S.-  nSSU0-  n[!        [        R"                  " U5      5      eS =n=pUR$                  R'                  5       nSoU;   o(       d  [        R                  " SU4SX45      [        R                  " U5      S[        R                  " 5       ;   d  [        R                  " U5      (       a  [        R                  " U5      OSS.-  nSSU0-  n[!        [        R"                  " U5      5      eS =pSoU;  o(       d  [        R                  " SU4SX45      [        R                  " U5      S[        R                  " 5       ;   d  [        R                  " U5      (       a  [        R                  " U5      OSS.-  nSSU0-  n[!        [        R"                  " U5      5      eS =pg )NWOLFPANEL_DEV_HOMEWOLFPANEL_API_MOCKr6   r   )ensure_dirsload_agent_tokenstore_credentials42wp_agent_token_valuer9   )z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)sr   cfg)r>   ry   rQ   rI   zassert %(py8)spy8zWOLFPANEL_SERVER_ID=42)in)z%(py1)s in %(py3)s	conf_textr   rR   r@   r}   r   )setenvstr	importlibconfigreloadload_configidentityr   r   r   rV   rW   rX   rY   rZ   r[   r\   r]   	conf_file	read_text)tmp_pathr,   r   config_moduler   r   r   r   rj   re   ra   @py_format7@py_format9r   r   rk   rc   s                    r   3test_store_credentials_persists_server_id_and_tokenr   T   s   +S];+S1"]#

#
#
%CIIc4!78C :$::$:::::: ::::::::::::::::C::::C::: :::$::::::::'')I#0y00000#000#000000y0000y0000000!222222!222!22222222222222222r   c           	     \   SS K n[        U SSS.5      nUR                  S5      nSnUR                  S/ S.U5        UR	                  SS	/ S
.U5        UR                  S/ S.U5        UR                  SS	S.U5        U Vs/ sH  oUR                  PM     nn/ SQovU:H  o(       d  [        R                  " SU4SXg45      S[        R                  " 5       ;   d  [        R                  " U5      (       a  [        R                  " U5      OS[        R                  " U5      S.-  n	SSU	0-  n
[        [        R                  " U
5      5      eS =pU GHU  nUR                   nSo" U5      oU:H  o(       Gd  [        R                  " SU4SX45      S[        R                  " 5       ;   d  [        R                  " U5      (       a  [        R                  " U5      OS[        R                  " U5      [        R                  " U5      [        R                  " U5      S[        R                  " 5       ;   d  [        R                  " U5      (       a  [        R                  " U5      OSS.-  nSSU0-  n[        [        R                  " U5      5      eS =n=n=pUR                   nSo" U5      nS nUUL o(       d  [        R                  " SU4SUU45      S[        R                  " 5       ;   d  [        R                  " U5      (       a  [        R                  " U5      OS[        R                  " U5      [        R                  " U5      [        R                  " U5      [        R                  " U5      S.-  nSS U0-  n[        [        R                  " U5      5      eS =n=n=n=nnGMX     g s  snf )!Nr   r   running
session_idr3   r4   ro   full)	scan_typeexpected_categoriesr5   )r   categoryitems)r   succeeded_categories)r   error)z7https://api.example.com/api/v1/agent/discovery/sessionsz4https://api.example.com/api/v1/agent/discovery/itemsz7https://api.example.com/api/v1/agent/discovery/completez5https://api.example.com/api/v1/agent/discovery/failedr9   rN   urlsrP   rR   r@   rC   )zP%(py6)s
{%(py6)s = %(py2)s
{%(py2)s = %(py0)s.get_header
}(%(py4)s)
} == %(py8)sr<   rq   )r>   r?   rH   rI   r   zassert %(py10)srz   rM   rD   rF   rG   rK   rL   )r)   r-   rS   open_discovery_sessionupload_discovery_itemscomplete_discoveryfail_discoveryrU   rV   rW   rX   rY   rZ   r[   r\   r]   r^   )r,   r)   r%   r_   rq   rr   rj   r`   rk   rc   r<   rb   re   rg   r   r   rf   rh   ri   s                       r   /test_discovery_endpoints_use_v1_paths_and_tokenr   l   s    Q).TUH%%&?@F E
!!r"RTYZ
!!b"QSXY
QKUS
S95A ()1JJD)     
 4  
  
 4  
 4  
      
   ~~7o7~o.7%77777.777777s7777s777~777o777.777777%7777%7777777~~6o6~o.6$6.$66666.$666666s6666s666~666o666.666$66666666  *s    N)c                  :    \ rS rSrSrS
S jrS rS rS rS r	Sr
g	)_RecordingClient   zJFake ApiClient that records calls; categories in `fail_on` raise ApiError.c                t    SSK Jn  X l        [        U5      U l        S U l        / U l        S U l        S U l        g )Nr   )ApiError)	r)   r   	_ApiErrorrx   fail_onopenedr   	completedfailed)r   r   r   s      r   __init___RecordingClient.__init__   s1    '!7|
r   c                    Xl         SSS.$ )N   r   r   )r   r   payloadrq   s      r   r   '_RecordingClient.open_discovery_session   s    955r   c                    US   U R                   ;   a  U R                  S5      eU R                  R                  U5        S[	        US   5      SS.$ )Nr   boomr   r   r   )r   accepted
item_count)r   r   r   r   lenr   s      r   r   '_RecordingClient.upload_discovery_items   sL    :$,,...((

'"S1A-BRSTTr   c                    Xl         SSSS.$ )Nr   r   r   )r   r3   r   )r   r   s      r   r   #_RecordingClient.complete_discovery   s     ;aHHr   c                    Xl         SSS.$ )Nr   r   r   )r   r   s      r   r   _RecordingClient.fail_discovery   s    844r   )r   r   r   r   r   r   N)r   )r   r   r   r   __doc__r   r   r   r   r   r   r   r   r   r   r      s     T6UI5r   r   c                 *   SSK Jn Jn  SS0SS0SS0/SS	/S
.n[        5       nU" S USU " U5      SS9nUR                  S   nSoeU:H  ow(       do  [
        R                  " SU4SXV45      [
        R                  " U5      [
        R                  " U5      S.-  nSSU0-  n	[        [
        R                  " U	5      5      eS =n=pvUR                  S   n/ SQoeU:H  ow(       do  [
        R                  " SU4SXV45      [
        R                  " U5      [
        R                  " U5      S.-  nSSU0-  n	[        [
        R                  " U	5      5      eS =n=pv[        S UR                   5       5      n
U
S   nSS0SS	0/oeU:H  ow(       do  [
        R                  " SU4SXV45      [
        R                  " U5      [
        R                  " U5      S.-  nSSU0-  n	[        [
        R                  " U	5      5      eS =n=pvUS   nSoeU:H  ow(       do  [
        R                  " SU4SXV45      [
        R                  " U5      [
        R                  " U5      S.-  nSSU0-  n	[        [
        R                  " U	5      5      eS =n=pvUR                  S   n[        U5      n/ SQoU:H  o(       d  [
        R                  " SU4SXl45      S[        R                  " 5       ;   d  [
        R                   " [        5      (       a  [
        R                  " [        5      OS[
        R                  " U5      [
        R                  " U5      [
        R                  " U5      S.-  nSS U0-  n[        [
        R                  " U5      5      eS =n=n=pUR                  S!   n/ oeU:H  ow(       do  [
        R                  " SU4SXV45      [
        R                  " U5      [
        R                  " U5      S.-  nSSU0-  n	[        [
        R                  " U	5      5      eS =n=pvg )"Nr   )categories_from_inventoryupload_inventoryrv   rw   nameab   P   )server_facts	databases
open_portsr   r/   rp   r   r9   z%(py1)s == %(py4)sry   rH   assert %(py6)srI   r   )r   r   r   c              3  8   #    U H  oS    S:X  d  M  Uv   M     g7f)r   r   Nr   ).0ps     r   	<genexpr>Atest_upload_inventory_drives_full_session_flow.<locals>.<genexpr>   s     JLqjM\,ILs   	r   valuer3   r   r   )z0%(py4)s
{%(py4)s = %(py0)s(%(py2)s)
} == %(py7)ssorted)r>   r?   rH   rB   zassert %(py9)srJ   failed_categories)discovery.uploadr   r   r   r   rV   rW   r[   r\   r]   nextr   r   r   rX   rY   rZ   )r   r   	inventoryr_   resultr   rb   rj   @py_format5r   portsr`   r   re   rd   rh   s                   r   .test_upload_inventory_drives_full_session_flowr      s
   L $S)smfc]32hI
 Fff7	B#F ==%///////%///%//////////==./^3^^3^^^^^^/^^^/^^^3^^^^^^^^JFLLJJE>;wmgr];;;;;;;;>;;;>;;;;;;;;;;;(*{*{***********{*******""#9: 6:;  @  @    ;     6   6   :   ;    @        /06B6B6666606660666B6666666r   c                 :   SSK Jn   [        S1S9nSS0/SS0/S	.nU " S US
USS9  UR                  nS oCULoU(       d  [        R
                  " SU4SX445      S[        R                  " 5       ;   d  [        R                  " U5      (       a  [        R                  " U5      OS[        R                  " U5      [        R                  " U5      S.-  nSSU0-  n[        [        R                  " U5      5      eS =n=pTUR                  S   nS/oXU:H  o(       do  [        R
                  " SU	4SX45      [        R                  " U5      [        R                  " U5      S.-  n
SSU
0-  n[        [        R                  " U5      5      eS =n=pUR                  S   nS/oXU:H  o(       do  [        R
                  " SU	4SX45      [        R                  " U5      [        R                  " U5      S.-  n
SSU
0-  n[        [        R                  " U5      5      eS =n=pg )Nr   r   r   r   docroot/ar   r5   )websitesr   r   r/   rp   is not)z5%(py2)s
{%(py2)s = %(py0)s.completed
} is not %(py5)sr_   r=   rA   rB   r   r   r9   r   r   r   rI   r   )r   r   r   r   rV   rW   rX   rY   rZ   r[   r\   r]   )r   r_   
categoriesr`   ra   rb   rc   rd   r   rj   r   r   s               r   -test_upload_inventory_reports_failed_categoryr      s^   1{m4F )401&#PJT66:SA'4'4'''''''''''6''''6''''''4'''''''23C
|C|CCCCC3CCC3CCC|CCCCCCC/0A[MAMAAAAA0AAA0AAAMAAAAAAAr   c                    SSK Jn   [        S1S9nU " S USSSS0/0SS	9  UR                  nS o2UL oD(       d  [        R
                  " S
U4SX#45      S[        R                  " 5       ;   d  [        R                  " U5      (       a  [        R                  " U5      OS[        R                  " U5      [        R                  " U5      S.-  nSSU0-  n[        [        R                  " U5      5      eS =n=pCUR                  nS o2ULoD(       d  [        R
                  " SU4SX#45      S[        R                  " 5       ;   d  [        R                  " U5      (       a  [        R                  " U5      OS[        R                  " U5      [        R                  " U5      S.-  nSSU0-  n[        [        R                  " U5      5      eS =n=pCUR                  S   nSoGU:H  o(       do  [        R
                  " SU4SXt45      [        R                  " U5      [        R                  " U5      S.-  n	SSU	0-  n
[        [        R                  " U
5      5      eS =n=pg )Nr   r   r   r   r   r   r   r/   rp   rD   )z1%(py2)s
{%(py2)s = %(py0)s.completed
} is %(py5)sr_   r=   rA   rB   r   )z2%(py2)s
{%(py2)s = %(py0)s.failed
} is not %(py5)sr   r   r9   r   r   r   rI   )r   r   r   r   rV   rW   rX   rY   rZ   r[   r\   r]   r   )r   r_   r`   ra   rb   rc   rd   r   rj   r   r   s              r   9test_upload_inventory_fails_session_when_nothing_succeedsr      sg   1zl3FT66J)T9J8K+LTWX #t#t###########6####6######t#######==$$$$$$$=$$$$$$6$$$$6$$$=$$$$$$$$$$==&+!+!+++++&+++&+++!+++++++r   c                  0    \ rS rSrSrS rS rS rS rSr	g)	_MiniMonkeypatch   z?Minimal monkeypatch for running this file directly (no pytest).c                    / U l         g r   _undor   s    r   r   _MiniMonkeypatch.__init__   s	    
r   c                h    U R                   R                  X[        X5      45        [        XU5        g r   )r   r   getattrr*   )r   objr   r   s       r   r*   _MiniMonkeypatch.setattr   s(    

3gc&89:5!r   c                    SS K nU R                  R                  SXR                  R	                  U5      45        X#R                  U'   g Nr   env)osr   r   environget)r   r   r   r  s       r   r   _MiniMonkeypatch.setenv   s5    

5$

t(<=> 

4r   c                   SS K n[        U R                  5       HI  u  p#nUS:X  a1  Uc  UR                  R	                  US 5        M-  XAR                  U'   M=  [        X#U5        MK     U R                  R                  5         g r  )r  reversedr   r	  popr*   clear)r   r  r  r   olds        r   undo_MiniMonkeypatch.undo   sb    &tzz2NCse|;JJNN4.'*JJt$3' 3 	

r   r   N)
r   r   r   r   r   r   r*   r   r  r   r   r   r   r   r      s    I"!r   r   __main__c              #  Z   #    U H"  u  pUR                  S 5      (       d  M  X4v   M$     g7f)test_N)
startswith)r   kvs      r   r   r      s%      241all76K2s   +
+r,   r   zPASS zFAIL r   )r$   zdict | Noner   )7r   
__future__r   builtinsrX   _pytest.assertion.rewrite	assertionrewriterV   ior   syspathlibr   pathinsertr   __file__resolveparentsBytesIOr   r-   rl   rs   r   r   r   r   r   r   r   r   r   tempfile	tracebackfailuresr   dictglobalsr   fnamefuncmp__code__co_varnamesco_argcountparamskwargsmkdtempprint	Exception	print_excr  exitr   r   r   <module>r9     s   #   	  
  3tH~--/77:UBC DBJJ L$3'30705 5@72	B	, < zH 	?002 t **+FT]]-F-FGF"$&F=!!%h&6&6&8!9F:	N6NE%/" GGI%& HH(Q"1 $  	"MHE%/"!	"
 GGIs$   F'GGGGG