*** Settings ***
Documentation       Resource DAG Linking and Discovery Tests
Library             Process
Library             OperatingSystem

*** Test Cases ***
Link Child And Verify Tree
    [Documentation]    Link a child resource and verify it appears in children list
    ${script}=    Catenate    SEPARATOR=\n
    ...    import json
    ...    from datetime import datetime, UTC
    ...    from sqlalchemy import create_engine, event
    ...    from sqlalchemy.pool import StaticPool
    ...    from sqlalchemy.orm import sessionmaker
    ...    from sqlalchemy.pool import StaticPool
    ...    from cleveragents.infrastructure.database.models import Base
    ...    from cleveragents.infrastructure.database.repositories import ResourceTypeRepository, ResourceRepository
    ...    from cleveragents.domain.models.core.resource_type import ResourceTypeSpec, ResourceKind, SandboxStrategy
    ...    from cleveragents.domain.models.core.resource import Resource, PhysVirt, ResourceCapabilities
    ...    engine = create_engine("sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool)
    ...    @event.listens_for(engine, "connect")
    ...    def _fk(conn, _): conn.cursor().execute("PRAGMA foreign_keys=ON")
    ...    Base.metadata.create_all(engine)
    ...    factory = sessionmaker(bind=engine)
    ...    shared_session = factory()
    ...    rt_repo = ResourceTypeRepository(lambda: shared_session)
    ...    res_repo = ResourceRepository(lambda: shared_session)
    ...    parent_spec = ResourceTypeSpec(name="robot/dag-parent", description="Parent", resource_kind=ResourceKind.PHYSICAL, sandbox_strategy=SandboxStrategy.NONE, user_addable=True, cli_args=[], parent_types=[], child_types=["robot/dag-child"], auto_discovery=None, equivalence=None, handler=None, capabilities={"read": True, "write": True, "sandbox": True, "checkpoint": False}, built_in=False)
    ...    child_spec = ResourceTypeSpec(name="robot/dag-child", description="Child", resource_kind=ResourceKind.PHYSICAL, sandbox_strategy=SandboxStrategy.NONE, user_addable=True, cli_args=[], parent_types=[], child_types=[], auto_discovery=None, equivalence=None, handler=None, capabilities={"read": True, "write": True, "sandbox": True, "checkpoint": False}, built_in=False)
    ...    rt_repo.create(parent_spec)
    ...    rt_repo.create(child_spec)
    ...    p = Resource(resource_id="01HDAGR0B0T0000000PARENT01", name=None, resource_type_name="robot/dag-parent", classification=PhysVirt.PHYSICAL, properties={}, location=None, capabilities=ResourceCapabilities(), created_at=datetime.now(tz=UTC), updated_at=datetime.now(tz=UTC))
    ...    c = Resource(resource_id="01HDAGR0B0T00000CHXND00001", name=None, resource_type_name="robot/dag-child", classification=PhysVirt.PHYSICAL, properties={}, location=None, capabilities=ResourceCapabilities(), created_at=datetime.now(tz=UTC), updated_at=datetime.now(tz=UTC))
    ...    res_repo.create(p)
    ...    res_repo.create(c)
    ...    res_repo.link_child("01HDAGR0B0T0000000PARENT01", "01HDAGR0B0T00000CHXND00001")
    ...    children = res_repo.get_children("01HDAGR0B0T0000000PARENT01")
    ...    assert len(children) == 1, f"Expected 1 child, got {len(children)}"
    ...    assert children[0].resource_id == "01HDAGR0B0T00000CHXND00001"
    ...    shared_session.close()
    ...    print("Link child and verify tree passed")
    ${result}=    Run Process    ${PYTHON}    -c    ${script}    timeout=60s    on_timeout=kill
    Should Be Equal As Integers    ${result.rc}    0    Link test failed: ${result.stderr}
    Should Contain    ${result.stdout}    Link child and verify tree passed

Cycle Detection Rejects A To B To A
    [Documentation]    Linking A->B then B->A should raise CycleDetectedError
    ${script}=    Catenate    SEPARATOR=\n
    ...    from datetime import datetime, UTC
    ...    from sqlalchemy import create_engine, event
    ...    from sqlalchemy.pool import StaticPool
    ...    from sqlalchemy.orm import sessionmaker
    ...    from sqlalchemy.pool import StaticPool
    ...    from cleveragents.infrastructure.database.models import Base
    ...    from cleveragents.infrastructure.database.repositories import ResourceTypeRepository, ResourceRepository, CycleDetectedError
    ...    from cleveragents.domain.models.core.resource_type import ResourceTypeSpec, ResourceKind, SandboxStrategy
    ...    from cleveragents.domain.models.core.resource import Resource, PhysVirt, ResourceCapabilities
    ...    engine = create_engine("sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool)
    ...    @event.listens_for(engine, "connect")
    ...    def _fk(conn, _): conn.cursor().execute("PRAGMA foreign_keys=ON")
    ...    Base.metadata.create_all(engine)
    ...    factory = sessionmaker(bind=engine)
    ...    shared_session = factory()
    ...    rt_repo = ResourceTypeRepository(lambda: shared_session)
    ...    res_repo = ResourceRepository(lambda: shared_session)
    ...    spec_a = ResourceTypeSpec(name="robot/cycle-a", description="Cycle A", resource_kind=ResourceKind.PHYSICAL, sandbox_strategy=SandboxStrategy.NONE, user_addable=True, cli_args=[], parent_types=[], child_types=["robot/cycle-b"], auto_discovery=None, equivalence=None, handler=None, capabilities={"read": True, "write": True, "sandbox": True, "checkpoint": False}, built_in=False)
    ...    spec_b = ResourceTypeSpec(name="robot/cycle-b", description="Cycle B", resource_kind=ResourceKind.PHYSICAL, sandbox_strategy=SandboxStrategy.NONE, user_addable=True, cli_args=[], parent_types=[], child_types=["robot/cycle-a"], auto_discovery=None, equivalence=None, handler=None, capabilities={"read": True, "write": True, "sandbox": True, "checkpoint": False}, built_in=False)
    ...    rt_repo.create(spec_a)
    ...    rt_repo.create(spec_b)
    ...    a = Resource(resource_id="01HDAGCYC000000000000000A1", name=None, resource_type_name="robot/cycle-a", classification=PhysVirt.PHYSICAL, properties={}, location=None, capabilities=ResourceCapabilities(), created_at=datetime.now(tz=UTC), updated_at=datetime.now(tz=UTC))
    ...    b = Resource(resource_id="01HDAGCYC000000000000000B1", name=None, resource_type_name="robot/cycle-b", classification=PhysVirt.PHYSICAL, properties={}, location=None, capabilities=ResourceCapabilities(), created_at=datetime.now(tz=UTC), updated_at=datetime.now(tz=UTC))
    ...    res_repo.create(a)
    ...    res_repo.create(b)
    ...    res_repo.link_child("01HDAGCYC000000000000000A1", "01HDAGCYC000000000000000B1")
    ...    try:
    ...    ${SPACE * 4}res_repo.link_child("01HDAGCYC000000000000000B1", "01HDAGCYC000000000000000A1")
    ...    ${SPACE * 4}assert False, "Should have raised CycleDetectedError"
    ...    except CycleDetectedError:
    ...    ${SPACE * 4}print("Cycle detection passed")
    ...    finally:
    ...    ${SPACE * 4}shared_session.close()
    ${result}=    Run Process    ${PYTHON}    -c    ${script}    timeout=60s    on_timeout=kill
    Should Be Equal As Integers    ${result.rc}    0    Cycle test failed: ${result.stderr}
    Should Contain    ${result.stdout}    Cycle detection passed

Auto Discover Children
    [Documentation]    Auto-discover creates child resources per type rules
    ${script}=    Catenate    SEPARATOR=\n
    ...    import json
    ...    from datetime import datetime, UTC
    ...    from sqlalchemy import create_engine, event
    ...    from sqlalchemy.pool import StaticPool
    ...    from sqlalchemy.orm import sessionmaker
    ...    from sqlalchemy.pool import StaticPool
    ...    from cleveragents.infrastructure.database.models import Base
    ...    from cleveragents.infrastructure.database.repositories import ResourceTypeRepository, ResourceRepository
    ...    from cleveragents.domain.models.core.resource_type import ResourceTypeSpec, ResourceKind, SandboxStrategy
    ...    from cleveragents.domain.models.core.resource import Resource, PhysVirt, ResourceCapabilities
    ...    engine = create_engine("sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool)
    ...    @event.listens_for(engine, "connect")
    ...    def _fk(conn, _): conn.cursor().execute("PRAGMA foreign_keys=ON")
    ...    Base.metadata.create_all(engine)
    ...    factory = sessionmaker(bind=engine)
    ...    shared_session = factory()
    ...    rt_repo = ResourceTypeRepository(lambda: shared_session)
    ...    res_repo = ResourceRepository(lambda: shared_session)
    ...    parent_spec = ResourceTypeSpec(name="robot/disc-parent", description="Discoverer", resource_kind=ResourceKind.PHYSICAL, sandbox_strategy=SandboxStrategy.NONE, user_addable=True, cli_args=[], parent_types=[], child_types=["robot/disc-child"], auto_discovery={"enabled": True, "rules": [{"type": "robot/disc-child", "pattern": "*"}]}, equivalence=None, handler=None, capabilities={"read": True, "write": True, "sandbox": True, "checkpoint": False}, built_in=False)
    ...    child_spec = ResourceTypeSpec(name="robot/disc-child", description="Discovered", resource_kind=ResourceKind.PHYSICAL, sandbox_strategy=SandboxStrategy.NONE, user_addable=True, cli_args=[], parent_types=[], child_types=[], auto_discovery=None, equivalence=None, handler=None, capabilities={"read": True, "write": True, "sandbox": True, "checkpoint": False}, built_in=False)
    ...    rt_repo.create(parent_spec)
    ...    rt_repo.create(child_spec)
    ...    p = Resource(resource_id="01HDAGR0B0TDSC00PARENT0001", name=None, resource_type_name="robot/disc-parent", classification=PhysVirt.PHYSICAL, properties={}, location=None, capabilities=ResourceCapabilities(), created_at=datetime.now(tz=UTC), updated_at=datetime.now(tz=UTC))
    ...    res_repo.create(p)
    ...    created = res_repo.auto_discover_children("01HDAGR0B0TDSC00PARENT0001")
    ...    assert len(created) >= 1, f"Expected >=1 children, got {len(created)}"
    ...    assert created[0].resource_type_name == "robot/disc-child"
    ...    children = res_repo.get_children("01HDAGR0B0TDSC00PARENT0001")
    ...    assert len(children) >= 1, f"Expected >=1 linked children, got {len(children)}"
    ...    shared_session.close()
    ...    print("Auto discover children passed")
    ${result}=    Run Process    ${PYTHON}    -c    ${script}    timeout=60s    on_timeout=kill
    Should Be Equal As Integers    ${result.rc}    0    Auto discover test failed: ${result.stderr}
    Should Contain    ${result.stdout}    Auto discover children passed

*** Keywords ***
