Files
2026-07-13 13:35:10 +08:00

218 lines
8.3 KiB
Python

import asyncio
import warnings
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
class TestAsyncUtilsControl:
"""Test nest_asyncio application control."""
def test_run_with_nest_asyncio_default(self):
"""Test run function applies nest_asyncio by default."""
from ragas.async_utils import run
async def test_func():
return "test"
with patch("ragas.async_utils.apply_nest_asyncio") as mock_apply:
result = run(test_func)
mock_apply.assert_called_once()
assert result == "test"
def test_run_without_nest_asyncio(self):
"""Test run function can skip nest_asyncio."""
from ragas.async_utils import run
async def test_func():
return "test"
with patch("ragas.async_utils.apply_nest_asyncio") as mock_apply:
result = run(test_func, allow_nest_asyncio=False)
mock_apply.assert_not_called()
assert result == "test"
class TestEvaluateAsyncControl:
"""Test the sync evaluate function with async options."""
def test_evaluate_with_nest_asyncio_default(self):
"""Test evaluate with default nest_asyncio behavior."""
with warnings.catch_warnings():
warnings.filterwarnings(
"ignore",
category=RuntimeWarning,
message=".*coroutine.*was never awaited",
)
with patch("ragas.async_utils.run") as mock_run:
mock_run.return_value = MagicMock()
from ragas import evaluate
evaluate(
dataset=MagicMock(),
metrics=[MagicMock()],
show_progress=False,
)
# Should call run() which applies nest_asyncio by default
mock_run.assert_called_once()
def test_evaluate_allow_nest_asyncio_true(self):
"""Test evaluate with allow_nest_asyncio=True explicitly."""
with warnings.catch_warnings():
warnings.filterwarnings(
"ignore",
category=RuntimeWarning,
message=".*coroutine.*was never awaited",
)
with patch("ragas.async_utils.run") as mock_run:
mock_run.return_value = MagicMock()
from ragas import evaluate
evaluate(
dataset=MagicMock(),
metrics=[MagicMock()],
show_progress=False,
allow_nest_asyncio=True,
)
# Should use run() which applies nest_asyncio
mock_run.assert_called_once()
def test_evaluate_allow_nest_asyncio_false(self):
"""Test evaluate with allow_nest_asyncio=False."""
with warnings.catch_warnings():
# Suppress RuntimeWarning about unawaited coroutines in tests
warnings.filterwarnings(
"ignore",
category=RuntimeWarning,
message=".*coroutine.*was never awaited",
)
with patch("asyncio.run") as mock_asyncio_run:
with patch("ragas.async_utils.run") as mock_run:
mock_asyncio_run.return_value = MagicMock()
from ragas import evaluate
evaluate(
dataset=MagicMock(),
metrics=[MagicMock()],
show_progress=False,
allow_nest_asyncio=False,
)
# Should use asyncio.run, not ragas.async_utils.run
mock_asyncio_run.assert_called_once()
mock_run.assert_not_called()
class TestAevaluateImport:
"""Test that aevaluate can be imported and is async."""
def test_aevaluate_importable(self):
"""Test that aevaluate can be imported."""
from ragas import aevaluate
assert callable(aevaluate)
assert asyncio.iscoroutinefunction(aevaluate)
def test_evaluate_has_allow_nest_asyncio_param(self):
"""Test that evaluate function has the new parameter."""
import inspect
from ragas import evaluate
sig = inspect.signature(evaluate)
assert "allow_nest_asyncio" in sig.parameters
assert sig.parameters["allow_nest_asyncio"].default is True
class TestNestAsyncioNotAppliedInAevaluate:
"""Test that aevaluate doesn't apply nest_asyncio."""
@pytest.mark.asyncio
async def test_aevaluate_no_nest_asyncio_applied(self):
"""Test that aevaluate doesn't call apply_nest_asyncio."""
with warnings.catch_warnings():
# Suppress RuntimeWarning about unawaited coroutines in tests
warnings.filterwarnings(
"ignore",
category=RuntimeWarning,
message=".*coroutine.*was never awaited",
)
# Mock all the dependencies to avoid actual API calls
with patch("ragas.evaluation.EvaluationDataset"):
with patch("ragas.evaluation.validate_required_columns"):
with patch("ragas.evaluation.validate_supported_metrics"):
with patch("ragas.evaluation.Executor") as mock_executor_class:
with patch("ragas.evaluation.new_group"):
with patch(
"ragas.async_utils.apply_nest_asyncio"
) as mock_apply:
# Mock executor
mock_executor = MagicMock()
mock_executor.aresults = AsyncMock(
return_value=[0.8]
)
mock_executor_class.return_value = mock_executor
# Mock dataset
mock_dataset_instance = MagicMock()
mock_dataset_instance.get_sample_type.return_value = MagicMock()
mock_dataset_instance.__iter__ = lambda x: iter([])
from ragas import aevaluate
try:
await aevaluate(
dataset=mock_dataset_instance,
metrics=[],
show_progress=False,
)
except Exception:
pass
# aevaluate should never call apply_nest_asyncio
mock_apply.assert_not_called()
class TestAsyncIntegration:
"""Basic integration tests for async scenarios."""
@pytest.mark.asyncio
async def test_aevaluate_in_running_loop(self):
"""Test aevaluate can be called when an event loop is already running."""
# This test runs with pytest-asyncio, so an event loop is running
from ragas import aevaluate
# Just test that the function can be called without RuntimeError
# We'll mock everything to avoid API calls
with patch("ragas.evaluation.EvaluationDataset"):
with patch("ragas.evaluation.validate_required_columns"):
with patch("ragas.evaluation.validate_supported_metrics"):
with patch("ragas.evaluation.Executor") as mock_executor_class:
with patch("ragas.evaluation.new_group"):
mock_executor = MagicMock()
mock_executor.aresults = AsyncMock(return_value=[])
mock_executor_class.return_value = mock_executor
try:
await aevaluate(
dataset=MagicMock(),
metrics=[],
show_progress=False,
)
# Should not raise RuntimeError about event loop
except Exception as e:
# We expect other exceptions due to mocking, but not RuntimeError
assert "event loop" not in str(e).lower()
assert "nest_asyncio" not in str(e).lower()