Files
wehub-resource-sync 85742ab165
CPU Test / Lint - next (push) Waiting to run
Dashboard / Chromatic (push) Waiting to run
CPU Test / Lint - fast (push) Waiting to run
CPU Test / Build documentation (push) Waiting to run
CPU Test / Test (Store, legacy, Python 3.10) (push) Waiting to run
CPU Test / Test (Utilities, legacy, Python 3.10) (push) Waiting to run
CPU Test / Test (Weave, legacy, Python 3.10) (push) Waiting to run
CPU Test / Test (AgentOps, stable, Python 3.11) (push) Waiting to run
CPU Test / Test (LLM proxy, stable, Python 3.11) (push) Waiting to run
CPU Test / Test (Others, stable, Python 3.11) (push) Waiting to run
CPU Test / Test (Store, stable, Python 3.11) (push) Waiting to run
CPU Test / Test (Utilities, stable, Python 3.11) (push) Waiting to run
CPU Test / Test (Weave, stable, Python 3.11) (push) Waiting to run
CPU Test / Test (AgentOps, stable, Python 3.12) (push) Waiting to run
CPU Test / Test (LLM proxy, stable, Python 3.12) (push) Waiting to run
CPU Test / Test (Others, stable, Python 3.12) (push) Waiting to run
CPU Test / Test (Store, stable, Python 3.12) (push) Waiting to run
CPU Test / Test (Utilities, stable, Python 3.12) (push) Waiting to run
CPU Test / Test (Weave, stable, Python 3.12) (push) Waiting to run
CPU Test / Test (AgentOps, latest, Python 3.13) (push) Waiting to run
CPU Test / Test (LLM proxy, latest, Python 3.13) (push) Waiting to run
CPU Test / Test (Others, latest, Python 3.13) (push) Waiting to run
CPU Test / Test (Store, latest, Python 3.13) (push) Waiting to run
CPU Test / Lint - slow (push) Waiting to run
CPU Test / Lint - JavaScript (push) Waiting to run
CPU Test / Test (AgentOps, legacy, Python 3.10) (push) Waiting to run
CPU Test / Test (LLM proxy, legacy, Python 3.10) (push) Waiting to run
CPU Test / Test (Others, legacy, Python 3.10) (push) Waiting to run
CPU Test / Test (Utilities, latest, Python 3.13) (push) Waiting to run
CPU Test / Test (Weave, latest, Python 3.13) (push) Waiting to run
CPU Test / Test (JavaScript) (push) Waiting to run
Deploy Documentation / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:44:17 +08:00

450 lines
13 KiB
Python

# Copyright (c) Microsoft. All rights reserved.
from __future__ import annotations
import os
import time
from itertools import count
from typing import TYPE_CHECKING, Any, AsyncGenerator, Dict, List, Mapping, Sequence
from unittest.mock import Mock
from uuid import uuid4
import pytest
import pytest_asyncio
from opentelemetry.sdk.trace import ReadableSpan
from pydantic import BaseModel, Field
from pytest import FixtureRequest
from agentlightning.store import collection_based
from agentlightning.store.base import LightningStore
from agentlightning.store.collection import DequeBasedQueue, DictBasedKeyValue, KeyValue, ListBasedCollection, Queue
from agentlightning.store.collection.base import Collection
from agentlightning.store.memory import InMemoryLightningStore
if TYPE_CHECKING:
from pymongo import AsyncMongoClient
from pymongo.asynchronous.database import AsyncDatabase
__all__ = [
"inmemory_store",
"inmemory_debounced_store",
"mongo_debounced_store",
"debounced_store",
"fake_time",
"mock_readable_span",
"sample_items",
"sample_collection",
"SampleItem",
"QueueItem",
"deque_queue",
"dict_key_value",
"dict_key_value_data",
"temporary_mongo_database",
"mongo_uri",
"mongo_client_kwargs",
]
mongo_uri = os.getenv("AGL_TEST_MONGO_URI", "mongodb://localhost:27017/?replicaSet=rs0")
mongo_client_kwargs: Dict[str, Any] = {"serverSelectionTimeoutMS": 5000}
@pytest.fixture
def inmemory_store() -> InMemoryLightningStore:
"""Create a fresh InMemoryLightningStore instance."""
return InMemoryLightningStore(scan_debounce_seconds=0)
@pytest.fixture
def inmemory_debounced_store(fake_time: _FakeTime) -> InMemoryLightningStore:
"""Create an InMemoryLightningStore configured with scan debouncing."""
return InMemoryLightningStore(scan_debounce_seconds=5.0)
@pytest_asyncio.fixture
async def mongo_store(temporary_mongo_database: AsyncDatabase[Any]):
"""Fixture for MongoDB store implementation."""
from agentlightning.store.mongo import MongoLightningStore
db = MongoLightningStore(
mongo_uri=mongo_uri,
mongo_client_kwargs=mongo_client_kwargs,
database_name=temporary_mongo_database.name,
scan_debounce_seconds=0,
)
try:
yield db
finally:
await db.close()
@pytest_asyncio.fixture
async def mongo_debounced_store(fake_time: _FakeTime, temporary_mongo_database: AsyncDatabase[Any]):
"""Fixture for MongoDB store implementation with scan debouncing."""
from agentlightning.store.mongo import MongoLightningStore
db = MongoLightningStore(
mongo_uri=mongo_uri,
mongo_client_kwargs=mongo_client_kwargs,
database_name=temporary_mongo_database.name,
scan_debounce_seconds=5.0,
)
try:
yield db
finally:
await db.close()
@pytest.fixture(
params=[
"inmemory_store",
pytest.param("mongo_store", marks=pytest.mark.mongo),
]
)
def store_fixture(request: FixtureRequest) -> AsyncGenerator[LightningStore, None]:
"""Parameterized fixture that provides different store implementations for testing."""
return request.getfixturevalue(request.param)
@pytest.fixture(
params=[
"inmemory_debounced_store",
pytest.param("mongo_debounced_store", marks=pytest.mark.mongo),
]
)
def debounced_store(request: FixtureRequest) -> LightningStore:
"""Parameterized fixture for debounced store implementations."""
return request.getfixturevalue(request.param)
@pytest.fixture
def mock_readable_span() -> ReadableSpan:
"""Create a mock ReadableSpan for testing."""
span = Mock()
span.name = "test_span"
context_counter = count(1)
def _make_context() -> Mock:
"""Generate a distinct span context each time it is requested."""
index = next(context_counter)
context = Mock()
context.trace_id = 111111
context.span_id = 222222 + index
context.is_remote = False
context.trace_state = {}
return context
# Mock context
span.get_span_context = Mock(side_effect=_make_context)
# Mock other attributes
span.parent = None
# Fix mock status to return proper string values
status_code_mock = Mock()
status_code_mock.name = "OK"
span.status = Mock(status_code=status_code_mock, description=None)
span.attributes = {"test": "value"}
span.events = []
span.links = []
span.start_time = time.time_ns()
span.end_time = time.time_ns() + 1000000
span.resource = Mock(attributes={}, schema_url="")
return span
class SampleItem(BaseModel):
partition: str
index: int
name: str
status: str
tags: List[str] = Field(default_factory=list)
score: float | None = None
rank: int | None = None
updated_time: float | None = None
payload: Dict[str, int] = Field(default_factory=dict)
metadata: str | None = None
class QueueItem(BaseModel):
idx: int
class _FakeTime:
"""Simple controllable clock for scan debouncing tests."""
def __init__(self, start: float = 0.0) -> None:
self._value = start
def time(self) -> float:
return self._value
def set(self, value: float) -> None:
self._value = value
def advance(self, delta: float) -> None:
self._value += delta
@pytest.fixture
def fake_time(monkeypatch: pytest.MonkeyPatch) -> _FakeTime:
"""Patch collection_based.time.time with a controllable clock."""
controller = _FakeTime()
monkeypatch.setattr(collection_based.time, "time", controller.time)
return controller
@pytest_asyncio.fixture
async def mongo_client():
from pymongo import AsyncMongoClient
client = AsyncMongoClient[Any](mongo_uri, **mongo_client_kwargs)
try:
await client.admin.command("ping")
except Exception as exc: # depends on external service
await client.close()
raise RuntimeError(f"MongoDB not available: {exc}")
try:
yield client
finally:
await client.close()
@pytest_asyncio.fixture
async def temporary_mongo_database(mongo_client: AsyncMongoClient[Any]):
"""Yield a temporary MongoDB database for integration tests."""
db_name = f"agentlightning-test-{uuid4().hex}"
db = mongo_client[db_name] # type: ignore
try:
yield db
finally:
await mongo_client.drop_database(db_name)
### Collection fixtures ###
@pytest.fixture()
def sample_items() -> List[SampleItem]:
return [
SampleItem(
partition="alpha",
index=1,
name="urgent-phase-one",
status="new",
tags=["core", "urgent"],
score=10.5,
rank=3,
updated_time=12.0,
payload={"priority": 10},
metadata="alpha-start",
),
SampleItem(
partition="alpha",
index=2,
name="phase-two",
status="running",
tags=["core"],
score=5.0,
rank=2,
updated_time=None,
payload={"priority": 5},
metadata=None,
),
SampleItem(
partition="alpha",
index=3,
name="delayed-phase",
status="blocked",
tags=["delayed"],
score=None,
rank=5,
updated_time=15.1,
payload={"priority": 8},
metadata="delayed-phase",
),
SampleItem(
partition="beta",
index=1,
name="beta-critical",
status="new",
tags=["beta", "urgent"],
score=8.0,
rank=1,
updated_time=7.0,
payload={"priority": 7},
metadata="beta critical",
),
SampleItem(
partition="beta",
index=2,
name="beta optional",
status="done",
tags=["beta"],
score=3.0,
rank=None,
updated_time=2.0,
payload={"priority": 1},
metadata="optional path",
),
SampleItem(
partition="gamma",
index=1,
name="gamma-phase",
status="running",
tags=[],
score=9.5,
rank=4,
updated_time=None,
payload={"priority": 9},
metadata="gamma-phase data",
),
SampleItem(
partition="gamma",
index=2,
name="gamma-late",
status="done",
tags=["late", "core"],
score=1.0,
rank=6,
updated_time=20.0,
payload={"priority": 2},
metadata="gamma late entry",
),
SampleItem(
partition="delta",
index=1,
name="delta misc",
status="archived",
tags=["misc"],
score=4.2,
rank=7,
updated_time=11.0,
payload={"priority": 3},
metadata="delta misc block",
),
]
### Generic collection fixtures ###
@pytest.fixture()
def sample_collection_memory(sample_items: Sequence[SampleItem]) -> ListBasedCollection[SampleItem]:
collection: Collection[SampleItem] = ListBasedCollection(list(sample_items), SampleItem, ("partition", "index"))
setattr(collection, "_test_backend", "memory")
return collection
@pytest_asyncio.fixture
async def sample_collection_mongo(temporary_mongo_database: AsyncDatabase[Any], sample_items: Sequence[SampleItem]):
from agentlightning.store.collection.mongo import MongoBasedCollection, MongoClientPool
async with MongoClientPool[Mapping[str, Any]](
mongo_uri=mongo_uri, mongo_client_kwargs=mongo_client_kwargs
) as client_pool:
collection = MongoBasedCollection(
client_pool,
temporary_mongo_database.name,
"sample-items",
"partition-123",
["partition", "index"],
SampleItem,
)
await collection.insert(sample_items)
setattr(collection, "_test_backend", "mongo")
yield collection
@pytest.fixture(
params=[
"memory",
pytest.param("mongo", marks=pytest.mark.mongo),
]
)
def sample_collection(request: pytest.FixtureRequest):
backend = request.param
return request.getfixturevalue("sample_collection_" + backend)
### Generic queue fixtures ###
@pytest.fixture
def deque_queue_memory() -> DequeBasedQueue[QueueItem]:
return DequeBasedQueue(QueueItem, [QueueItem(idx=i) for i in range(3)])
@pytest_asyncio.fixture
async def deque_queue_mongo(temporary_mongo_database: AsyncDatabase[Any]):
from agentlightning.store.collection.mongo import MongoBasedQueue, MongoClientPool
async with MongoClientPool[Mapping[str, Any]](
mongo_uri=mongo_uri, mongo_client_kwargs=mongo_client_kwargs
) as client_pool:
queue = MongoBasedQueue[QueueItem](
client_pool,
temporary_mongo_database.name,
"queue-items",
"partition-1",
QueueItem,
)
await queue.enqueue([QueueItem(idx=i) for i in range(3)])
yield queue
@pytest.fixture(
params=[
"memory",
pytest.param("mongo", marks=pytest.mark.mongo),
]
)
def deque_queue(request: pytest.FixtureRequest) -> AsyncGenerator[Queue[QueueItem], None]:
backend = request.param
return request.getfixturevalue("deque_queue_" + backend)
### Generic key-value fixtures ###
@pytest.fixture()
def dict_key_value_data() -> Dict[str, int]:
return {"alpha": 1, "beta": 2}
@pytest.fixture()
def dict_key_value_memory(dict_key_value_data: Dict[str, int]) -> DictBasedKeyValue[str, int]:
return DictBasedKeyValue(dict_key_value_data)
@pytest_asyncio.fixture
async def dict_key_value_mongo(temporary_mongo_database: AsyncDatabase[Any], dict_key_value_data: Dict[str, int]):
from agentlightning.store.collection.mongo import MongoBasedKeyValue, MongoClientPool
async with MongoClientPool[Mapping[str, Any]](
mongo_uri=mongo_uri, mongo_client_kwargs=mongo_client_kwargs
) as client_pool:
key_value = MongoBasedKeyValue[str, int](
client_pool,
temporary_mongo_database.name,
"key-value-items",
"partition-1",
str,
int,
)
for key, value in dict_key_value_data.items():
await key_value.set(key, value)
yield key_value
@pytest.fixture(
params=[
"memory",
pytest.param("mongo", marks=pytest.mark.mongo),
]
)
def dict_key_value(request: pytest.FixtureRequest) -> AsyncGenerator[KeyValue[str, int], None]:
backend = request.param
return request.getfixturevalue("dict_key_value_" + backend)