mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-02 13:48:09 +00:00
279 lines
8.7 KiB
Python
279 lines
8.7 KiB
Python
"""Tests for FastAPI-style dependency injection in event handlers."""
|
|
|
|
import asyncio
|
|
|
|
import pytest
|
|
|
|
from crewai.events import Depends, crewai_event_bus
|
|
from crewai.events.base_events import BaseEvent
|
|
|
|
|
|
class DependsTestEvent(BaseEvent):
|
|
"""Test event for dependency tests."""
|
|
|
|
value: int = 0
|
|
type: str = "test_event"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_basic_dependency():
|
|
"""Test that handler with dependency runs after its dependency."""
|
|
execution_order = []
|
|
|
|
with crewai_event_bus.scoped_handlers():
|
|
|
|
@crewai_event_bus.on(DependsTestEvent)
|
|
def setup(source, event: DependsTestEvent):
|
|
execution_order.append("setup")
|
|
|
|
@crewai_event_bus.on(DependsTestEvent, Depends(setup))
|
|
def process(source, event: DependsTestEvent):
|
|
execution_order.append("process")
|
|
|
|
event = DependsTestEvent(value=1)
|
|
future = crewai_event_bus.emit("test_source", event)
|
|
|
|
if future:
|
|
await asyncio.wrap_future(future)
|
|
|
|
assert execution_order == ["setup", "process"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_multiple_dependencies():
|
|
"""Test handler with multiple dependencies."""
|
|
execution_order = []
|
|
|
|
with crewai_event_bus.scoped_handlers():
|
|
|
|
@crewai_event_bus.on(DependsTestEvent)
|
|
def setup_a(source, event: DependsTestEvent):
|
|
execution_order.append("setup_a")
|
|
|
|
@crewai_event_bus.on(DependsTestEvent)
|
|
def setup_b(source, event: DependsTestEvent):
|
|
execution_order.append("setup_b")
|
|
|
|
@crewai_event_bus.on(
|
|
DependsTestEvent, depends_on=[Depends(setup_a), Depends(setup_b)]
|
|
)
|
|
def process(source, event: DependsTestEvent):
|
|
execution_order.append("process")
|
|
|
|
event = DependsTestEvent(value=1)
|
|
future = crewai_event_bus.emit("test_source", event)
|
|
|
|
if future:
|
|
await asyncio.wrap_future(future)
|
|
|
|
# setup_a and setup_b can run in any order (same level)
|
|
assert "process" in execution_order
|
|
assert execution_order.index("process") > execution_order.index("setup_a")
|
|
assert execution_order.index("process") > execution_order.index("setup_b")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_chain_of_dependencies():
|
|
"""Test chain of dependencies (A -> B -> C)."""
|
|
execution_order = []
|
|
|
|
with crewai_event_bus.scoped_handlers():
|
|
|
|
@crewai_event_bus.on(DependsTestEvent)
|
|
def handler_a(source, event: DependsTestEvent):
|
|
execution_order.append("handler_a")
|
|
|
|
@crewai_event_bus.on(DependsTestEvent, depends_on=Depends(handler_a))
|
|
def handler_b(source, event: DependsTestEvent):
|
|
execution_order.append("handler_b")
|
|
|
|
@crewai_event_bus.on(DependsTestEvent, depends_on=Depends(handler_b))
|
|
def handler_c(source, event: DependsTestEvent):
|
|
execution_order.append("handler_c")
|
|
|
|
event = DependsTestEvent(value=1)
|
|
future = crewai_event_bus.emit("test_source", event)
|
|
|
|
if future:
|
|
await asyncio.wrap_future(future)
|
|
|
|
assert execution_order == ["handler_a", "handler_b", "handler_c"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_async_handler_with_dependency():
|
|
"""Test async handler with dependency on sync handler."""
|
|
execution_order = []
|
|
|
|
with crewai_event_bus.scoped_handlers():
|
|
|
|
@crewai_event_bus.on(DependsTestEvent)
|
|
def sync_setup(source, event: DependsTestEvent):
|
|
execution_order.append("sync_setup")
|
|
|
|
@crewai_event_bus.on(DependsTestEvent, depends_on=Depends(sync_setup))
|
|
async def async_process(source, event: DependsTestEvent):
|
|
await asyncio.sleep(0.01)
|
|
execution_order.append("async_process")
|
|
|
|
event = DependsTestEvent(value=1)
|
|
future = crewai_event_bus.emit("test_source", event)
|
|
|
|
if future:
|
|
await asyncio.wrap_future(future)
|
|
|
|
assert execution_order == ["sync_setup", "async_process"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_mixed_handlers_with_dependencies():
|
|
"""Test mix of sync and async handlers with dependencies."""
|
|
execution_order = []
|
|
|
|
with crewai_event_bus.scoped_handlers():
|
|
|
|
@crewai_event_bus.on(DependsTestEvent)
|
|
def setup(source, event: DependsTestEvent):
|
|
execution_order.append("setup")
|
|
|
|
@crewai_event_bus.on(DependsTestEvent, depends_on=Depends(setup))
|
|
def sync_process(source, event: DependsTestEvent):
|
|
execution_order.append("sync_process")
|
|
|
|
@crewai_event_bus.on(DependsTestEvent, depends_on=Depends(setup))
|
|
async def async_process(source, event: DependsTestEvent):
|
|
await asyncio.sleep(0.01)
|
|
execution_order.append("async_process")
|
|
|
|
@crewai_event_bus.on(
|
|
DependsTestEvent, depends_on=[Depends(sync_process), Depends(async_process)]
|
|
)
|
|
def finalize(source, event: DependsTestEvent):
|
|
execution_order.append("finalize")
|
|
|
|
event = DependsTestEvent(value=1)
|
|
future = crewai_event_bus.emit("test_source", event)
|
|
|
|
if future:
|
|
await asyncio.wrap_future(future)
|
|
|
|
assert execution_order[0] == "setup"
|
|
assert "finalize" in execution_order
|
|
assert execution_order.index("finalize") > execution_order.index("sync_process")
|
|
assert execution_order.index("finalize") > execution_order.index("async_process")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_independent_handlers_run_concurrently():
|
|
"""Test that handlers without dependencies can run concurrently."""
|
|
execution_order = []
|
|
|
|
with crewai_event_bus.scoped_handlers():
|
|
|
|
@crewai_event_bus.on(DependsTestEvent)
|
|
async def handler_a(source, event: DependsTestEvent):
|
|
await asyncio.sleep(0.01)
|
|
execution_order.append("handler_a")
|
|
|
|
@crewai_event_bus.on(DependsTestEvent)
|
|
async def handler_b(source, event: DependsTestEvent):
|
|
await asyncio.sleep(0.01)
|
|
execution_order.append("handler_b")
|
|
|
|
event = DependsTestEvent(value=1)
|
|
future = crewai_event_bus.emit("test_source", event)
|
|
|
|
if future:
|
|
await asyncio.wrap_future(future)
|
|
|
|
assert len(execution_order) == 2
|
|
assert "handler_a" in execution_order
|
|
assert "handler_b" in execution_order
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_circular_dependency_detection():
|
|
"""Test that circular dependencies are detected and raise an error."""
|
|
from crewai.events.handler_graph import CircularDependencyError, build_execution_plan
|
|
|
|
def handler_a(source, event: DependsTestEvent):
|
|
pass
|
|
|
|
def handler_b(source, event: DependsTestEvent):
|
|
pass
|
|
|
|
def handler_c(source, event: DependsTestEvent):
|
|
pass
|
|
|
|
handlers = [handler_a, handler_b, handler_c]
|
|
dependencies = {
|
|
handler_a: [Depends(handler_b)],
|
|
handler_b: [Depends(handler_c)],
|
|
handler_c: [Depends(handler_a)],
|
|
}
|
|
|
|
with pytest.raises(CircularDependencyError, match="Circular dependency"):
|
|
build_execution_plan(handlers, dependencies)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_handler_without_dependency_runs_normally():
|
|
"""Test that handlers without dependencies still work as before."""
|
|
execution_order = []
|
|
|
|
with crewai_event_bus.scoped_handlers():
|
|
|
|
@crewai_event_bus.on(DependsTestEvent)
|
|
def simple_handler(source, event: DependsTestEvent):
|
|
execution_order.append("simple_handler")
|
|
|
|
event = DependsTestEvent(value=1)
|
|
future = crewai_event_bus.emit("test_source", event)
|
|
|
|
if future:
|
|
await asyncio.wrap_future(future)
|
|
|
|
assert execution_order == ["simple_handler"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_depends_equality():
|
|
"""Test Depends equality and hashing."""
|
|
|
|
def handler_a(source, event):
|
|
pass
|
|
|
|
def handler_b(source, event):
|
|
pass
|
|
|
|
dep_a1 = Depends(handler_a)
|
|
dep_a2 = Depends(handler_a)
|
|
dep_b = Depends(handler_b)
|
|
|
|
assert dep_a1 == dep_a2
|
|
assert hash(dep_a1) == hash(dep_a2)
|
|
|
|
assert dep_a1 != dep_b
|
|
assert hash(dep_a1) != hash(dep_b)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_aemit_ignores_dependencies():
|
|
"""Test that aemit only processes async handlers (no dependency support yet)."""
|
|
execution_order = []
|
|
|
|
with crewai_event_bus.scoped_handlers():
|
|
|
|
@crewai_event_bus.on(DependsTestEvent)
|
|
def sync_handler(source, event: DependsTestEvent):
|
|
execution_order.append("sync_handler")
|
|
|
|
@crewai_event_bus.on(DependsTestEvent)
|
|
async def async_handler(source, event: DependsTestEvent):
|
|
execution_order.append("async_handler")
|
|
|
|
event = DependsTestEvent(value=1)
|
|
await crewai_event_bus.aemit("test_source", event)
|
|
|
|
assert execution_order == ["async_handler"]
|