Ctrl K
ComunidadGuías…

test/network/test_client_lifecycle.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Copyright (c) 2026, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors
#
# SPDX-License-Identifier: Apache-2.0

"""Async-context-manager lifecycle for ``HubClient`` and ``AgentClient``.

Both classes implement ``__aenter__`` / ``__aexit__`` so an ``async
with`` block runs the right cleanup on exit:

* ``HubClient.__aexit__`` calls ``self.close()`` — cancels the link's
  receive task and closes the transport.
* ``AgentClient.__aexit__`` calls ``self.unregister()`` — round-trip
  to the hub to remove the agent from its registry, plus marks the
  client locally disconnected.

Cleanup must run on both clean exit and exception paths.
"""

import pytest

from ag2 import Agent
from ag2.knowledge import MemoryKnowledgeStore
from ag2.network import (
    Hub,
    HubClient,
    LocalLink,
    Passport,
    Resume,
)
from ag2.network.errors import NotFoundError
from ag2.testing import TestConfig


def _agent(name: str) -> Agent:
    return Agent(name=name, config=TestConfig())


@pytest.mark.asyncio
class TestClientLifecycle:
    """async-with on HubClient and AgentClient runs the right cleanup."""

    async def test_hub_client_close_runs_on_exit(self) -> None:
        store = MemoryKnowledgeStore()
        hub = await Hub.open(store, ttl_sweep_interval=0)
        link = LocalLink(hub)

        async with HubClient(link, hub=hub) as hc:
            assert not hc._closed

        assert hc._closed

        await hub.close()

    async def test_agent_client_unregister_runs_on_exit(self) -> None:
        """On block exit, the agent is unregistered from the hub registry
        and marked locally disconnected."""
        store = MemoryKnowledgeStore()
        hub = await Hub.open(store, ttl_sweep_interval=0)
        link = LocalLink(hub)

        async with HubClient(link, hub=hub) as hc:
            ac = await hc.register(_agent("alice"), Passport(name="alice"), Resume())
            agent_id = ac.agent_id
            assert not ac._disconnected

            async with ac:
                # Hub knows about alice while the block is open.
                passport = await hub.get_agent(agent_id)
                assert passport.name == "alice"

            # On block exit: unregister() called → marked disconnected
            # locally and removed from hub registry.
            assert ac._disconnected
            with pytest.raises(NotFoundError):
                await hub.get_agent(agent_id)

        await hub.close()

    async def test_cleanup_runs_on_exception(self) -> None:
        """If the body raises, both __aexit__ paths still run."""
        store = MemoryKnowledgeStore()
        hub = await Hub.open(store, ttl_sweep_interval=0)
        link = LocalLink(hub)

        boom = RuntimeError("boom")

        try:
            async with HubClient(link, hub=hub) as hc:
                ac = await hc.register(_agent("alice"), Passport(name="alice"), Resume())
                agent_id = ac.agent_id
                async with ac:
                    raise boom
        except RuntimeError as caught:
            assert caught is boom

        # AgentClient.__aexit__ ran despite the raise: locally disconnected
        # and removed from the hub registry.
        assert ac._disconnected
        with pytest.raises(NotFoundError):
            await hub.get_agent(agent_id)

        # HubClient.__aexit__ also ran: transport closed.
        assert hc._closed

        await hub.close()