
    iT0              	       6   d Z ddlZddlmc mZ ddlZddlZddl	Z	ddl
Z
ddlZddlZddlZddlZddlZ e	j        e          j                                        Zej                                        Ze	j                                        Z e	j         ej        d eedz                                Z e ej        dd                    Zde Z e	j         ej        d eed	z                                Zed
z  Z edz  Z!e!"                                s e#de! d          de	j        fdZ$defdZ% e$            Z& e%e&          Z'e&r ee&          n
 ee          Z(e&duZ)d Z* e*            Z+ej,        -                    e) d          Z.ej,        -                    e+ d          Z/d Z0d Z1d#dZ2d$dZ3 ej4        dd          d             Z5 ej4        d          d             Z6 ej4        d           d!             Z7d#d"Z8dS )%a7  
Shared pytest fixtures for webui-mvp tests.

TEST ISOLATION:
  Tests run against a SEPARATE server instance on port 8788 with a
  completely separate state directory. Production data is never touched.
  The test state dir is wiped before each full test run and again on teardown.

PATH DISCOVERY:
  No hardcoded paths. Discovery order:
    1. Environment variables (HERMES_WEBUI_AGENT_DIR, HERMES_WEBUI_PYTHON, etc.)
    2. Sibling checkout heuristics relative to this repo
    3. Common install paths (~/.hermes/hermes-agent)
    4. System python3 as a last resort
    NHERMES_HOME.hermesHERMES_WEBUI_TEST_PORT8788zhttp://127.0.0.1:HERMES_WEBUI_TEST_STATE_DIRzwebui-mvp-testztest-workspacez	server.pyzserver.py not found at z8. Is conftest.py in the tests/ subdirectory of the repo?returnc                     t          j        dd          t          t          dz            t          t          j        dz            t          t          dz  dz            t          t          dz            g} | D ]l}|st          j        |          	                                }|
                                r-|dz  
                                r|                                c S md S )NHERMES_WEBUI_AGENT_DIR zhermes-agentr   zrun_agent.py)osgetenvstrr   	REPO_ROOTparentHOMEpathlibPath
expanduserexistsresolve)
candidatescps      +/home/ubuntu/hermes-webui/tests/conftest.py_discover_agent_dirr   3   s    
	*B//K.())I~-..D9~-..D>!""J    	LOO&&((88:: 	1~-5577 	99;;4    c                 l   t          j        d          rt          j        d          S | r.| dz  dz  dz  }|                                rt          |          S t          dz  dz  dz  }|                                rt          |          S t          j        d          pt          j        d          pdS )NHERMES_WEBUI_PYTHONvenvbinpythonz.venvpython3)r   r   r   r   r   shutilwhich)	agent_dirvenv_py
local_venvs      r   _discover_pythonr(   D   s    	y&'' 0y.///  f$u,x7>> 	 w<<W$u,x7J :<	""Ifl8&<&<I	Ir   c                      t           sdS 	 ddl} dD ]}|                     |           dS # t          t          f$ r Y dS w xY w)z;Verify hermes-agent Python modules are actually importable.Fr   N)z	cron.jobsztools.skills_toolT)HERMES_AGENT	importlibimport_moduleImportErrorModuleNotFoundError)r+   mods     r   _check_agent_modulesr0   [   ss     u5 	) 	)C##C((((t,-   uus   + A A z6hermes-agent not found (skipping agent-dependent test)reasonz>hermes-agent Python modules not importable (cron, skills_tool)c                 ^    |                      dd           |                      dd           d S )Nmarkersz7requires_agent: skip when hermes-agent dir is not foundzPrequires_agent_modules: skip when hermes-agent Python modules are not importable)addinivalue_line)configs    r   pytest_configurer7   t   s6    
I'`aaa
I'yzzzzzr   c                     t           rdS h d}t          j                            d          }d}|D ]%}|j        |v r|                    |           |dz  }&|rt          d| d           dS dS )	aQ  Auto-skip agent-dependent tests when hermes-agent is not available.

    Instead of requiring markers on every test function, we pattern-match
    test names to known categories that depend on hermes-agent modules.
    This keeps the test files clean and ensures new cron/skills tests
    get auto-skipped without manual annotation.
    N>   test_crons_listtest_skills_listtest_workspace_renametest_cron_create_successtest_workspace_add_validtest_skills_content_knowntest_crons_output_real_jobtest_crons_run_nonexistenttest_cron_delete_unknown_404test_crons_output_limit_paramtest_skill_delete_unknown_404 test_approval_submit_and_respond test_cron_update_unknown_job_404 test_skill_save_delete_roundtrip!test_crons_output_requires_job_id!test_skills_content_requires_name!test_skills_search_returns_subset#test_chat_stream_opens_successfully#test_crons_list_has_required_fields#test_new_session_inherits_workspace$test_skills_list_has_required_fields(test_new_session_inherits_last_workspace-test_last_workspace_updates_on_session_updatez%requires hermes-agent (not installed)r1   r      u$   
⚠️  hermes-agent not found — z' agent-dependent tests will be skipped
)AGENT_MODULES_AVAILABLEpytestmarkskipname
add_markerprint)r6   items_AGENT_DEPENDENT_TESTSskip_markerskippeditems         r   pytest_collection_modifyitemsr]   x   s      
  < +""*Q"RRKG  9...OOK(((qLG iggggghhhhhi ir   c                 "   t          j        |pi                                           }t          j                            | |z   |ddi          }	 t          j                            |d          5 }t          j        |                                          cd d d            S # 1 swxY w Y   d S # t          j	        j
        $ rH}	 t          j        |                                          cY d }~S # t          $ r
 i cY cY d }~S w xY wd }~ww xY w)NzContent-Typezapplication/json)dataheaders
   timeout)jsondumpsencodeurllibrequestRequesturlopenloadsreaderror	HTTPError	Exception)basepathbodyr_   reqres          r   _postrv      sb   :djb!!((**D
.
 
 t$9K(L !  C^##C#44 	(:affhh''	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	( 	(<!   	:affhh'''''''' 	 	 	IIIIIIII	s`   !B2 2&B%B2 %B))B2 ,B)-B2 2D%C2,D2D>D	?DDD		D   c                    t          j                     |z   }t          j                     |k     r	 t          j                            | dz   d          5 }t	          j        |                                                              d          dk    r	 d d d            dS 	 d d d            n# 1 swxY w Y   n$# t          $ r t          j	        d           Y nw xY wt          j                     |k     dS )	Nz/health   rb   statusokTg333333?F)
timerg   rh   rj   rd   rk   rl   getro   sleep)rp   rc   deadlinert   s       r   _wait_for_serverr      s<   y{{W$H
)++
 
 	''y(8!'DD  :affhh''++H55==               =                               	 	 	JsOOOOO	 )++
 
  5s;   $B8 ?B,B8  B8 ,B00B8 3B04B8 8CCsessionT)scopeautousec               #   f  K   t                                           rt          j        t                      t                               d           t
                              d           t          dz  } t           dz  }|                                 r)|                                s|                    |            t           dz                      dd           t          j	        
                                }|                    t          t                    dt          t                     t          t
                    dt          t                     d           t          rt          t                    |d	<   t          j        t"          t          t$                    gt&          |t          j        t          j        
          }t+          t,          d          sQ|                                 t1          j        dt           dt$           dt"           dt           dt&           d           |V  |                                 	 |                    d           n)# t          j        $ r |                                 Y nw xY w	 t          j        t                      dS # t:          $ r Y dS w xY w)z
    Start an isolated test server on TEST_PORT with a clean state directory.
    Paths are discovered dynamically -- no hardcoded absolute path assumptions.
    T)parentsskillscron)r   exist_okz	127.0.0.1zopenai/gpt-5.4-mini)HERMES_WEBUI_PORTHERMES_WEBUI_HOSTHERMES_WEBUI_STATE_DIRHERMES_WEBUI_DEFAULT_WORKSPACEHERMES_WEBUI_DEFAULT_MODELr   r
   )cwdenvstdoutstderrrw   rb   zTest server on port z) did not start within 20s.
  server.py : z
  python    : z
  agent dir : z
  workdir   : 
   N)TEST_STATE_DIRr   r#   rmtreemkdirTEST_WORKSPACEr   
symlink_tor   environcopyupdater   	TEST_PORTr*   
subprocessPopenVENV_PYTHONSERVER_SCRIPTWORKDIRDEVNULLr   	TEST_BASEkillrR   fail	terminatewaitTimeoutExpiredro   )real_skillstest_skillsr   procs       r   test_serverr      s       &n%%%&&&&&& )K!H,K ,K$6$6$8$8 ,{+++ f##D4#@@@
*//

CJJ*-i..*5*-n*=*=*-n*=*=*?*-n*=*=      :(+L(9(9$%	c-(()!!  D Ir222 
		)9 ) )*) )() ) *) ) %	) ) )	
 	
 	
 JJJNN		!	$   		n%%%%%   s$   I #JJJ" "
J0/J0)r   c                      t           S N)r    r   r   base_urlr     s    r   )r   c               #   J  K   g } | V  | D ]+}	 t          t          dd|i           # t          $ r Y (w xY w	 t          t          d           n# t          $ r Y nw xY w	 t          dz  }|                    t          t                    d           dS # t          $ r Y dS w xY w)z
    Yields a list for tests to register created session IDs.
    Deletes all registered sessions after each test.
    Resets last_workspace to the test workspace to prevent state bleed.
    z/api/session/delete
session_idz"/api/sessions/cleanup_zero_messagezlast_workspace.txtzutf-8)encodingN)rv   r   ro   r   
write_textr   r   )createdsidlast_ws_files      r   cleanup_test_sessionsr     s       G
MMM  	)2\34GHHHH 	 	 	D	i=>>>>   %(<<N 3 3gFFFFF   s/   '
44A 
AA3B 
B"!B"c                     i }|rt          |          |d<   t          t          d|          }|d         d         }t          j        |d         d                   }|                     |           ||fS )z
    Create a session on the test server and register it for cleanup.

    Usage:
        def test_something(cleanup_test_sessions):
            sid, ws = make_session_tracked(cleanup_test_sessions)
    	workspacez/api/session/newr   r   )r   rv   r   r   r   append)created_listwsrr   dr   ws_paths         r   make_session_trackedr   :  sw     D	 $GG[i+T22A
I,|
$Cl1Y<455G<r   r   )rw   )9__doc__builtins@py_builtins_pytest.assertion.rewrite	assertionrewrite
@pytest_arrd   r   r   r#   r   r|   urllib.requestrg   urllib.errorrR   r   __file__r   r   	TESTS_DIRr   homer   r   r   r   intr   r   r   r   r   r   RuntimeErrorr   r(   r*   r   r   AGENT_AVAILABLEr0   rQ   rS   skipifrequires_agentrequires_agent_modulesr7   r]   rv   r   fixturer   r   r   r   r   r   r   <module>r      s                 				                 W\(##*2244	%%''	\  gl929]CCy8H4I4IJJKK YRY7@@AA	0Y00	ibi!C&&''     "22 K' 
,	A- 	A 	A 	A  W\    "
J3 
J 
J 
J 
J #"$$--  ,
?##l


Y
 d*   /.00  ##C $    ++K ,   
{ { {5i 5i 5it   	 	 	 	 i...@ @ /.@J i     !    :     r   