a7d6d88f6f
CI / changes (push) Has been cancelled
CI / cd libs/checkpoint (push) Has been cancelled
CI / cd libs/checkpoint-conformance (push) Has been cancelled
CI / cd libs/checkpoint-postgres (push) Has been cancelled
CI / cd libs/checkpoint-sqlite (push) Has been cancelled
CI / cd libs/cli (push) Has been cancelled
CI / cd libs/prebuilt (push) Has been cancelled
CI / cd libs/sdk-py (push) Has been cancelled
CI / cd libs/langgraph (push) Has been cancelled
CI / Check SDK methods matching (push) Has been cancelled
CI / Check CLI schema hasn't changed #3.13 (push) Has been cancelled
CI / CLI integration test (push) Has been cancelled
CI / sdk-py integration test (push) Has been cancelled
CI / CI Success (push) Has been cancelled
baseline / benchmark (push) Has been cancelled
Deploy Redirects to GitHub Pages / deploy (push) Has been cancelled
227 lines
6.9 KiB
Python
227 lines
6.9 KiB
Python
import os
|
|
from collections.abc import AsyncIterator, Iterator
|
|
from uuid import UUID
|
|
|
|
import pytest
|
|
import redis
|
|
from langgraph.cache.base import BaseCache
|
|
from langgraph.cache.memory import InMemoryCache
|
|
from langgraph.cache.redis import RedisCache
|
|
from langgraph.cache.sqlite import SqliteCache
|
|
from langgraph.checkpoint.base import BaseCheckpointSaver
|
|
from langgraph.store.base import BaseStore
|
|
from pytest_mock import MockerFixture
|
|
|
|
from langgraph.types import Durability
|
|
from tests.conftest_checkpointer import (
|
|
_checkpointer_memory,
|
|
_checkpointer_memory_migrate_sends,
|
|
_checkpointer_postgres,
|
|
_checkpointer_postgres_aio,
|
|
_checkpointer_postgres_aio_pipe,
|
|
_checkpointer_postgres_aio_pool,
|
|
_checkpointer_postgres_pipe,
|
|
_checkpointer_postgres_pool,
|
|
_checkpointer_sqlite,
|
|
_checkpointer_sqlite_aes,
|
|
_checkpointer_sqlite_aio,
|
|
)
|
|
from tests.conftest_store import (
|
|
_store_memory,
|
|
_store_postgres,
|
|
_store_postgres_aio,
|
|
_store_postgres_aio_pipe,
|
|
_store_postgres_aio_pool,
|
|
_store_postgres_pipe,
|
|
_store_postgres_pool,
|
|
)
|
|
|
|
NO_DOCKER = os.getenv("NO_DOCKER", "false") == "true"
|
|
|
|
|
|
@pytest.fixture
|
|
def anyio_backend():
|
|
return "asyncio"
|
|
|
|
|
|
@pytest.fixture()
|
|
def deterministic_uuids(mocker: MockerFixture) -> MockerFixture:
|
|
side_effect = (
|
|
UUID(f"00000000-0000-4000-8000-{i:012}", version=4) for i in range(10000)
|
|
)
|
|
return mocker.patch("uuid.uuid4", side_effect=side_effect)
|
|
|
|
|
|
@pytest.fixture(params=["sync", "async", "exit"])
|
|
def durability(request: pytest.FixtureRequest) -> Durability:
|
|
return request.param
|
|
|
|
|
|
@pytest.fixture(
|
|
scope="function",
|
|
params=["sqlite", "memory"] if NO_DOCKER else ["sqlite", "memory", "redis"],
|
|
)
|
|
def cache(request: pytest.FixtureRequest) -> Iterator[BaseCache]:
|
|
if request.param == "sqlite":
|
|
yield SqliteCache(path=":memory:")
|
|
elif request.param == "memory":
|
|
yield InMemoryCache()
|
|
elif request.param == "redis":
|
|
# Get worker ID for parallel test isolation
|
|
worker_id = getattr(request.config, "workerinput", {}).get("workerid", "master")
|
|
|
|
redis_client = redis.Redis(
|
|
host="localhost", port=6379, db=0, decode_responses=False
|
|
)
|
|
# Use worker-specific prefix to avoid cache pollution between parallel tests
|
|
cache = RedisCache(redis_client, prefix=f"test:cache:{worker_id}:")
|
|
yield cache
|
|
|
|
try:
|
|
# Only clear keys with our specific prefix
|
|
pattern = f"test:cache:{worker_id}:*"
|
|
keys = redis_client.keys(pattern)
|
|
if keys:
|
|
redis_client.delete(*keys)
|
|
except Exception:
|
|
pass
|
|
else:
|
|
raise ValueError(f"Unknown cache type: {request.param}")
|
|
|
|
|
|
@pytest.fixture(
|
|
scope="function",
|
|
params=["in_memory"]
|
|
if NO_DOCKER
|
|
else ["in_memory", "postgres", "postgres_pipe", "postgres_pool"],
|
|
)
|
|
def sync_store(request: pytest.FixtureRequest) -> Iterator[BaseStore]:
|
|
store_name = request.param
|
|
if store_name is None:
|
|
yield None
|
|
elif store_name == "in_memory":
|
|
with _store_memory() as store:
|
|
yield store
|
|
elif store_name == "postgres":
|
|
with _store_postgres() as store:
|
|
yield store
|
|
elif store_name == "postgres_pipe":
|
|
with _store_postgres_pipe() as store:
|
|
yield store
|
|
elif store_name == "postgres_pool":
|
|
with _store_postgres_pool() as store:
|
|
yield store
|
|
else:
|
|
raise NotImplementedError(f"Unknown store {store_name}")
|
|
|
|
|
|
@pytest.fixture(
|
|
scope="function",
|
|
params=["in_memory"]
|
|
if NO_DOCKER
|
|
else ["in_memory", "postgres_aio", "postgres_aio_pipe", "postgres_aio_pool"],
|
|
)
|
|
async def async_store(request: pytest.FixtureRequest) -> AsyncIterator[BaseStore]:
|
|
store_name = request.param
|
|
if store_name is None:
|
|
yield None
|
|
elif store_name == "in_memory":
|
|
with _store_memory() as store:
|
|
yield store
|
|
elif store_name == "postgres_aio":
|
|
async with _store_postgres_aio() as store:
|
|
yield store
|
|
elif store_name == "postgres_aio_pipe":
|
|
async with _store_postgres_aio_pipe() as store:
|
|
yield store
|
|
elif store_name == "postgres_aio_pool":
|
|
async with _store_postgres_aio_pool() as store:
|
|
yield store
|
|
else:
|
|
raise NotImplementedError(f"Unknown store {store_name}")
|
|
|
|
|
|
@pytest.fixture(
|
|
scope="function",
|
|
params=[
|
|
"memory",
|
|
"sqlite",
|
|
"sqlite_aes",
|
|
]
|
|
if NO_DOCKER
|
|
else [
|
|
"memory",
|
|
"memory_migrate_sends",
|
|
"sqlite",
|
|
"sqlite_aes",
|
|
"postgres",
|
|
"postgres_pipe",
|
|
"postgres_pool",
|
|
],
|
|
)
|
|
def sync_checkpointer(
|
|
request: pytest.FixtureRequest,
|
|
) -> Iterator[BaseCheckpointSaver]:
|
|
checkpointer_name = request.param
|
|
if checkpointer_name == "memory":
|
|
with _checkpointer_memory() as checkpointer:
|
|
yield checkpointer
|
|
elif checkpointer_name == "memory_migrate_sends":
|
|
with _checkpointer_memory_migrate_sends() as checkpointer:
|
|
yield checkpointer
|
|
elif checkpointer_name == "sqlite":
|
|
with _checkpointer_sqlite() as checkpointer:
|
|
yield checkpointer
|
|
elif checkpointer_name == "sqlite_aes":
|
|
with _checkpointer_sqlite_aes() as checkpointer:
|
|
yield checkpointer
|
|
elif checkpointer_name == "postgres":
|
|
with _checkpointer_postgres() as checkpointer:
|
|
yield checkpointer
|
|
elif checkpointer_name == "postgres_pipe":
|
|
with _checkpointer_postgres_pipe() as checkpointer:
|
|
yield checkpointer
|
|
elif checkpointer_name == "postgres_pool":
|
|
with _checkpointer_postgres_pool() as checkpointer:
|
|
yield checkpointer
|
|
else:
|
|
raise NotImplementedError(f"Unknown checkpointer: {checkpointer_name}")
|
|
|
|
|
|
@pytest.fixture(
|
|
scope="function",
|
|
params=[
|
|
"memory",
|
|
"sqlite_aio",
|
|
]
|
|
if NO_DOCKER
|
|
else [
|
|
"memory",
|
|
"sqlite_aio",
|
|
"postgres_aio",
|
|
"postgres_aio_pipe",
|
|
"postgres_aio_pool",
|
|
],
|
|
)
|
|
async def async_checkpointer(
|
|
request: pytest.FixtureRequest,
|
|
) -> AsyncIterator[BaseCheckpointSaver]:
|
|
checkpointer_name = request.param
|
|
if checkpointer_name == "memory":
|
|
with _checkpointer_memory() as checkpointer:
|
|
yield checkpointer
|
|
elif checkpointer_name == "sqlite_aio":
|
|
async with _checkpointer_sqlite_aio() as checkpointer:
|
|
yield checkpointer
|
|
elif checkpointer_name == "postgres_aio":
|
|
async with _checkpointer_postgres_aio() as checkpointer:
|
|
yield checkpointer
|
|
elif checkpointer_name == "postgres_aio_pipe":
|
|
async with _checkpointer_postgres_aio_pipe() as checkpointer:
|
|
yield checkpointer
|
|
elif checkpointer_name == "postgres_aio_pool":
|
|
async with _checkpointer_postgres_aio_pool() as checkpointer:
|
|
yield checkpointer
|
|
else:
|
|
raise NotImplementedError(f"Unknown checkpointer: {checkpointer_name}")
|