feat(tests): refactor async coroutine handling with shared run_async function
This commit is contained in:
@@ -1,9 +1,31 @@
|
||||
"""Shared fixtures for EvoScientist tests."""
|
||||
|
||||
|
||||
import asyncio
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def run_async(coro):
|
||||
"""Run an async coroutine safely, cancelling pending tasks before closing.
|
||||
|
||||
This prevents 'Event loop is closed' errors from asyncio.Queue cleanup
|
||||
when tasks are still waiting on Queue.get() at teardown time.
|
||||
"""
|
||||
loop = asyncio.new_event_loop()
|
||||
try:
|
||||
return loop.run_until_complete(coro)
|
||||
finally:
|
||||
# Cancel all pending tasks so Queue getters don't raise on close
|
||||
pending = asyncio.all_tasks(loop)
|
||||
for task in pending:
|
||||
task.cancel()
|
||||
if pending:
|
||||
loop.run_until_complete(asyncio.gather(*pending, return_exceptions=True))
|
||||
loop.run_until_complete(loop.shutdown_asyncgens())
|
||||
loop.close()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_tool_call():
|
||||
"""A minimal tool call dict."""
|
||||
|
||||
@@ -13,13 +13,7 @@ from EvoScientist.channels.channel_manager import ChannelManager
|
||||
from EvoScientist.channels.base import Channel, OutgoingMessage
|
||||
|
||||
|
||||
def _run(coro):
|
||||
"""Run an async coroutine safely, creating a fresh event loop."""
|
||||
loop = asyncio.new_event_loop()
|
||||
try:
|
||||
return loop.run_until_complete(coro)
|
||||
finally:
|
||||
loop.close()
|
||||
from tests.conftest import run_async as _run
|
||||
|
||||
|
||||
def _drain_queue(q):
|
||||
|
||||
@@ -46,12 +46,7 @@ from EvoScientist.channels.formatter import convert_markdown
|
||||
# Helpers
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
def _run(coro):
|
||||
loop = asyncio.new_event_loop()
|
||||
try:
|
||||
return loop.run_until_complete(coro)
|
||||
finally:
|
||||
loop.close()
|
||||
from tests.conftest import run_async as _run
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@@ -9,13 +9,7 @@ from EvoScientist.channels.channel_manager import ChannelManager
|
||||
from EvoScientist.channels.base import Channel, OutboundMessage
|
||||
|
||||
|
||||
def _run(coro):
|
||||
"""Run an async coroutine safely, creating a fresh event loop."""
|
||||
loop = asyncio.new_event_loop()
|
||||
try:
|
||||
return loop.run_until_complete(coro)
|
||||
finally:
|
||||
loop.close()
|
||||
from tests.conftest import run_async as _run
|
||||
|
||||
|
||||
class _FakeConfig:
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"""Tests for DingTalk channel implementation."""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
@@ -10,13 +9,7 @@ from EvoScientist.channels.dingtalk.channel import DingTalkChannel, DingTalkConf
|
||||
from EvoScientist.channels.base import ChannelError, OutboundMessage
|
||||
|
||||
|
||||
def _run(coro):
|
||||
"""Run an async coroutine safely, creating a fresh event loop."""
|
||||
loop = asyncio.new_event_loop()
|
||||
try:
|
||||
return loop.run_until_complete(coro)
|
||||
finally:
|
||||
loop.close()
|
||||
from tests.conftest import run_async as _run
|
||||
|
||||
|
||||
class TestDingTalkConfig:
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
"""Tests for Discord channel implementation."""
|
||||
|
||||
import asyncio
|
||||
|
||||
import pytest
|
||||
|
||||
from EvoScientist.channels.discord.channel import DiscordChannel, DiscordConfig
|
||||
from EvoScientist.channels.base import ChannelError
|
||||
|
||||
|
||||
def _run(coro):
|
||||
"""Run an async coroutine safely, creating a fresh event loop."""
|
||||
loop = asyncio.new_event_loop()
|
||||
try:
|
||||
return loop.run_until_complete(coro)
|
||||
finally:
|
||||
loop.close()
|
||||
from tests.conftest import run_async as _run
|
||||
|
||||
|
||||
class TestDiscordConfig:
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"""Tests for Feishu channel implementation."""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
@@ -16,13 +15,7 @@ from EvoScientist.channels.feishu.channel import (
|
||||
from EvoScientist.channels.base import ChannelError, OutboundMessage
|
||||
|
||||
|
||||
def _run(coro):
|
||||
"""Run an async coroutine safely, creating a fresh event loop."""
|
||||
loop = asyncio.new_event_loop()
|
||||
try:
|
||||
return loop.run_until_complete(coro)
|
||||
finally:
|
||||
loop.close()
|
||||
from tests.conftest import run_async as _run
|
||||
|
||||
|
||||
class TestFeishuConfig:
|
||||
|
||||
@@ -7,13 +7,7 @@ from EvoScientist.channels.bus.events import InboundMessage, OutboundMessage
|
||||
from EvoScientist.channels.bus.message_bus import MessageBus
|
||||
|
||||
|
||||
def _run(coro):
|
||||
"""Run an async coroutine safely, creating a fresh event loop."""
|
||||
loop = asyncio.new_event_loop()
|
||||
try:
|
||||
return loop.run_until_complete(coro)
|
||||
finally:
|
||||
loop.close()
|
||||
from tests.conftest import run_async as _run
|
||||
|
||||
|
||||
# ── Event tests ──
|
||||
|
||||
+1
-11
@@ -1,6 +1,5 @@
|
||||
"""Tests for EvoScientist.sessions — thread CRUD, ID generation, helpers."""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
@@ -21,16 +20,7 @@ from EvoScientist.sessions import (
|
||||
)
|
||||
|
||||
|
||||
def _run(coro):
|
||||
"""Run an async coroutine synchronously (resilient to closed loops)."""
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
if loop.is_closed():
|
||||
raise RuntimeError("closed")
|
||||
except RuntimeError:
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
return loop.run_until_complete(coro)
|
||||
from tests.conftest import run_async as _run
|
||||
|
||||
|
||||
class TestGenerateThreadId(unittest.TestCase):
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
"""Tests for Slack channel implementation."""
|
||||
|
||||
import asyncio
|
||||
|
||||
import pytest
|
||||
|
||||
from EvoScientist.channels.slack.channel import SlackChannel, SlackConfig
|
||||
from EvoScientist.channels.base import ChannelError
|
||||
|
||||
|
||||
def _run(coro):
|
||||
"""Run an async coroutine safely, creating a fresh event loop."""
|
||||
loop = asyncio.new_event_loop()
|
||||
try:
|
||||
return loop.run_until_complete(coro)
|
||||
finally:
|
||||
loop.close()
|
||||
from tests.conftest import run_async as _run
|
||||
|
||||
|
||||
class TestSlackConfig:
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
"""Tests for Telegram channel implementation."""
|
||||
|
||||
import asyncio
|
||||
|
||||
import pytest
|
||||
|
||||
from EvoScientist.channels.telegram.channel import TelegramChannel, TelegramConfig
|
||||
from EvoScientist.channels.base import ChannelError
|
||||
|
||||
|
||||
def _run(coro):
|
||||
"""Run an async coroutine safely, creating a fresh event loop."""
|
||||
loop = asyncio.new_event_loop()
|
||||
try:
|
||||
return loop.run_until_complete(coro)
|
||||
finally:
|
||||
loop.close()
|
||||
from tests.conftest import run_async as _run
|
||||
|
||||
|
||||
class TestTelegramConfig:
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"""Tests for ToolErrorHandlerMiddleware."""
|
||||
|
||||
import asyncio
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
@@ -150,8 +149,10 @@ class TestWrapToolCallAsync:
|
||||
def setup_method(self):
|
||||
self.mw = ToolErrorHandlerMiddleware()
|
||||
|
||||
def _run(self, coro):
|
||||
return asyncio.get_event_loop().run_until_complete(coro)
|
||||
@staticmethod
|
||||
def _run(coro):
|
||||
from tests.conftest import run_async
|
||||
return run_async(coro)
|
||||
|
||||
def test_success_passes_through(self):
|
||||
expected = ToolMessage(content="ok", tool_call_id="tc_001", name="t")
|
||||
|
||||
@@ -22,13 +22,7 @@ from EvoScientist.channels.wechat.crypto import (
|
||||
from EvoScientist.channels.base import ChannelError
|
||||
|
||||
|
||||
def _run(coro):
|
||||
"""Run an async coroutine safely, creating a fresh event loop."""
|
||||
loop = asyncio.new_event_loop()
|
||||
try:
|
||||
return loop.run_until_complete(coro)
|
||||
finally:
|
||||
loop.close()
|
||||
from tests.conftest import run_async as _run
|
||||
|
||||
|
||||
# ── Config tests ──────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user