*** Settings ***
Documentation     Integration smoke tests for async resource cleanup (#321).
...               Validates that AsyncResourceTracker, enhanced bridge cleanup,
...               A2aEventQueue.close(), and StateManager.close() work end-to-end
...               by executing small Python driver scripts in a subprocess.

Library           OperatingSystem
Library           Process
Library           String
Resource          ${CURDIR}/common.resource

Test Setup        Setup Async Cleanup Test Environment
Test Teardown     Cleanup Async Cleanup Test Environment

*** Variables ***
${PYTHON}             python
${WORKSPACE_ROOT}     ${CURDIR}/..
${TEST_FILE}          ${EMPTY}
${TEST_OUTPUT}        ${EMPTY}
${TIMEOUT}            30s

*** Test Cases ***
Test AsyncResourceTracker Register And Close
    [Documentation]    Register a resource and close it via close_all
    ${script} =    Catenate    SEPARATOR=\n
    ...    import sys, asyncio
    ...    sys.path.insert(0, '${WORKSPACE_ROOT}/src')
    ...    from cleveragents.core.async_cleanup import AsyncResourceTracker
    ...
    ...    class FakeResource:
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}def __init__(self): self.closed = False
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}async def close(self): self.closed = True
    ...
    ...    async def main():
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}tracker = AsyncResourceTracker()
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}res = FakeResource()
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}tracker.register("test-res", res)
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}assert tracker.open_count == 1
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}await tracker.close_all()
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}assert res.closed
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}assert tracker.open_count == 0
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}print("PASS: register and close", flush=True)
    ...
    ...    asyncio.run(main())
    Create File    ${TEST_FILE}    ${script}
    ${result} =    Run Process    ${PYTHON}    ${TEST_FILE}
    ...    stdout=${TEST_OUTPUT}    stderr=STDOUT    timeout=${TIMEOUT}    on_timeout=kill
    Should Be Equal As Integers    ${result.rc}    0
    ${output} =    Get File    ${TEST_OUTPUT}
    Should Contain    ${output}    PASS: register and close

Test AsyncResourceTracker Timeout Warning
    [Documentation]    Verify forced-termination warning for slow resource
    ${script} =    Catenate    SEPARATOR=\n
    ...    import sys, asyncio, logging
    ...    sys.path.insert(0, '${WORKSPACE_ROOT}/src')
    ...    from cleveragents.core.async_cleanup import AsyncResourceTracker
    ...
    ...    handler = logging.StreamHandler(sys.stdout)
    ...    handler.setLevel(logging.WARNING)
    ...    logging.getLogger("cleveragents.core.async_cleanup").addHandler(handler)
    ...
    ...    class SlowResource:
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}async def close(self): await asyncio.sleep(10)
    ...
    ...    async def main():
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}tracker = AsyncResourceTracker()
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}tracker.register("slow", SlowResource())
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}await tracker.close_all(timeout=0.05)
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}assert len(tracker.timed_out_resources) == 1
    ...    ${SPACE}${SPACE}${SPACE}${SPACE}print("PASS: timeout warning", flush=True)
    ...
    ...    asyncio.run(main())
    Create File    ${TEST_FILE}    ${script}
    ${result} =    Run Process    ${PYTHON}    ${TEST_FILE}
    ...    stdout=${TEST_OUTPUT}    stderr=STDOUT    timeout=${TIMEOUT}    on_timeout=kill
    Should Be Equal As Integers    ${result.rc}    0
    ${output} =    Get File    ${TEST_OUTPUT}
    Should Contain    ${output}    PASS: timeout warning

Test A2aEventQueue Close
    [Documentation]    Verify A2aEventQueue.close() removes subscriptions
    ${script} =    Catenate    SEPARATOR=\n
    ...    import sys
    ...    sys.path.insert(0, '${WORKSPACE_ROOT}/src')
    ...    from cleveragents.a2a.events import A2aEventQueue
    ...
    ...    q = A2aEventQueue()
    ...    q.subscribe_local(lambda e: None)
    ...    q.subscribe_local(lambda e: None)
    ...    assert len(q._subscriptions) == 2
    ...    q.close()
    ...    assert len(q._subscriptions) == 0
    ...    print("PASS: event queue close", flush=True)
    Create File    ${TEST_FILE}    ${script}
    ${result} =    Run Process    ${PYTHON}    ${TEST_FILE}
    ...    stdout=${TEST_OUTPUT}    stderr=STDOUT    timeout=${TIMEOUT}    on_timeout=kill
    Should Be Equal As Integers    ${result.rc}    0
    ${output} =    Get File    ${TEST_OUTPUT}
    Should Contain    ${output}    PASS: event queue close

Test StateManager Close
    [Documentation]    Verify StateManager.close() marks manager as closed
    ${script} =    Catenate    SEPARATOR=\n
    ...    import sys, tempfile
    ...    sys.path.insert(0, '${WORKSPACE_ROOT}/src')
    ...    from pathlib import Path
    ...    from cleveragents.langgraph.state import StateManager, GraphState
    ...
    ...    d = tempfile.mkdtemp()
    ...    mgr = StateManager(initial_state=GraphState(), checkpoint_dir=Path(d))
    ...    assert not mgr.is_closed
    ...    mgr.close()
    ...    assert mgr.is_closed
    ...    print("PASS: state manager close", flush=True)
    Create File    ${TEST_FILE}    ${script}
    ${result} =    Run Process    ${PYTHON}    ${TEST_FILE}
    ...    stdout=${TEST_OUTPUT}    stderr=STDOUT    timeout=${TIMEOUT}    on_timeout=kill
    Should Be Equal As Integers    ${result.rc}    0
    ${output} =    Get File    ${TEST_OUTPUT}
    Should Contain    ${output}    PASS: state manager close

*** Keywords ***
Setup Async Cleanup Test Environment
    [Documentation]    Create temp dir for test scripts
    ${temp_dir} =    Evaluate    tempfile.mkdtemp()    modules=tempfile
    Set Test Variable    ${TEMP_DIR}    ${temp_dir}
    Set Test Variable    ${TEST_FILE}    ${temp_dir}/async_cleanup_test.py
    Set Test Variable    ${TEST_OUTPUT}    ${temp_dir}/async_cleanup_output.txt

Cleanup Async Cleanup Test Environment
    [Documentation]    Remove temp dir
    Run Keyword And Ignore Error    Remove File    ${TEST_FILE}
    Run Keyword And Ignore Error    Remove File    ${TEST_OUTPUT}
    Run Keyword And Ignore Error    Remove Directory    ${TEMP_DIR}    recursive=True
