218 lines
8.3 KiB
Python
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()
|