Standard tests for LangChain implementations
—
Generic testing suites for key-value store implementations with comprehensive CRUD operations, bulk operations, and both synchronous and asynchronous support. These tests ensure that key-value stores meet LangChain's storage interface requirements.
Comprehensive testing suite for synchronous key-value store implementations.
from typing import Generic, TypeVar, List
from langchain_tests.integration_tests import BaseStoreSyncTests
V = TypeVar('V')
class BaseStoreSyncTests(BaseStandardTests, Generic[V]):
"""Synchronous key-value store testing suite."""
# Required fixtures
@pytest.fixture
def kv_store(self):
"""Empty key-value store instance for testing. Must be implemented by test class."""
@pytest.fixture
def three_values(self) -> List[V]:
"""Three example values for testing. Must be implemented by test class."""
# Fixture validation
def test_three_values(self) -> None:
"""Validate that the three_values fixture provides correct test data."""
# Store state tests
def test_kv_store_is_empty(self) -> None:
"""Verify that the key-value store starts empty."""
def test_store_still_empty(self) -> None:
"""Verify that the store is properly cleaned up after tests."""
# Basic CRUD operations
def test_set_and_get_values(self) -> None:
"""Test setting and getting values from the store."""
def test_delete_values(self) -> None:
"""Test deleting individual values from the store."""
def test_delete_bulk_values(self) -> None:
"""Test bulk deletion of multiple values."""
def test_delete_missing_keys(self) -> None:
"""Test deletion behavior when keys don't exist."""
# Idempotency and consistency tests
def test_set_values_is_idempotent(self) -> None:
"""Test that setting the same key multiple times is idempotent."""
def test_get_can_get_same_value(self) -> None:
"""Test that getting the same key returns consistent values."""
def test_overwrite_values_by_key(self) -> None:
"""Test overwriting existing values with new ones."""
# Key iteration
def test_yield_keys(self) -> None:
"""Test iterating over all keys in the store."""from typing import List
import pytest
from langchain_tests.integration_tests import BaseStoreSyncTests
from my_integration import MyKeyValueStore
class TestMyKeyValueStore(BaseStoreSyncTests[str]):
@pytest.fixture
def kv_store(self):
# Create a fresh store instance for each test
store = MyKeyValueStore(
connection_url="redis://localhost:6379/1",
namespace="test_kv"
)
yield store
# Cleanup after test
store.clear()
@pytest.fixture
def three_values(self) -> List[str]:
return ["value1", "value2", "value3"]Comprehensive testing suite for asynchronous key-value store implementations.
from typing import Generic, TypeVar, List
from langchain_tests.integration_tests import BaseStoreAsyncTests
V = TypeVar('V')
class BaseStoreAsyncTests(BaseStandardTests, Generic[V]):
"""Asynchronous key-value store testing suite."""
# Required fixtures
@pytest.fixture
async def kv_store(self):
"""Empty async key-value store instance for testing. Must be implemented by test class."""
@pytest.fixture
def three_values(self) -> List[V]:
"""Three example values for testing. Must be implemented by test class."""
# Async fixture validation
async def test_three_values(self) -> None:
"""Validate that the three_values fixture provides correct test data."""
# Async store state tests
async def test_kv_store_is_empty(self) -> None:
"""Verify that the async key-value store starts empty."""
async def test_store_still_empty(self) -> None:
"""Verify that the async store is properly cleaned up after tests."""
# Async CRUD operations
async def test_set_and_get_values(self) -> None:
"""Test async setting and getting values from the store."""
async def test_delete_values(self) -> None:
"""Test async deletion of individual values from the store."""
async def test_delete_bulk_values(self) -> None:
"""Test async bulk deletion of multiple values."""
async def test_delete_missing_keys(self) -> None:
"""Test async deletion behavior when keys don't exist."""
# Async idempotency and consistency tests
async def test_set_values_is_idempotent(self) -> None:
"""Test that async setting the same key multiple times is idempotent."""
async def test_get_can_get_same_value(self) -> None:
"""Test that async getting the same key returns consistent values."""
async def test_overwrite_values_by_key(self) -> None:
"""Test async overwriting existing values with new ones."""
# Async key iteration
async def test_yield_keys(self) -> None:
"""Test async iteration over all keys in the store."""from typing import List, Dict, Any
import pytest
from langchain_tests.integration_tests import BaseStoreAsyncTests
from my_integration import MyAsyncKeyValueStore
class TestMyAsyncKeyValueStore(BaseStoreAsyncTests[Dict[str, Any]]):
@pytest.fixture
async def kv_store(self):
# Create a fresh async store instance for each test
store = await MyAsyncKeyValueStore.create(
connection_url="redis://localhost:6379/1",
namespace="test_async_kv"
)
yield store
# Cleanup after test
await store.clear()
await store.close()
@pytest.fixture
def three_values(self) -> List[Dict[str, Any]]:
return [
{"id": 1, "name": "Alice", "role": "admin"},
{"id": 2, "name": "Bob", "role": "user"},
{"id": 3, "name": "Charlie", "role": "guest"}
]The key-value store testing framework supports generic types, allowing you to test stores with any value type:
class TestStringStore(BaseStoreSyncTests[str]):
@pytest.fixture
def three_values(self) -> List[str]:
return ["hello", "world", "test"]class TestJSONStore(BaseStoreSyncTests[Dict[str, Any]]):
@pytest.fixture
def three_values(self) -> List[Dict[str, Any]]:
return [
{"key": "value1", "count": 1},
{"key": "value2", "count": 2},
{"key": "value3", "count": 3}
]from dataclasses import dataclass
@dataclass
class CustomObject:
name: str
value: int
tags: List[str]
class TestCustomObjectStore(BaseStoreSyncTests[CustomObject]):
@pytest.fixture
def three_values(self) -> List[CustomObject]:
return [
CustomObject("obj1", 100, ["tag1", "tag2"]),
CustomObject("obj2", 200, ["tag2", "tag3"]),
CustomObject("obj3", 300, ["tag1", "tag3"])
]Key-value store implementations typically use string keys. The testing framework validates various key patterns:
def test_simple_string_keys(self):
"""Test with simple alphanumeric keys."""
keys = ["key1", "key2", "key3"]import uuid
def test_uuid_keys(self):
"""Test with UUID-based keys."""
keys = [str(uuid.uuid4()) for _ in range(3)]def test_hierarchical_keys(self):
"""Test with hierarchical key structures."""
keys = ["user:123:profile", "user:123:settings", "user:456:profile"]The testing framework validates bulk operations for performance:
def test_bulk_set_operations(self) -> None:
"""Test setting multiple key-value pairs in bulk."""
def test_bulk_get_operations(self) -> None:
"""Test getting multiple values in bulk."""
def test_bulk_delete_operations(self) -> None:
"""Test deleting multiple keys in bulk."""Key-value store tests verify proper error handling:
def test_connection_error_handling(self) -> None:
"""Test behavior when store backend is unavailable."""
def test_timeout_handling(self) -> None:
"""Test handling of operation timeouts."""def test_serialization_error_handling(self) -> None:
"""Test handling of values that cannot be serialized."""
def test_deserialization_error_handling(self) -> None:
"""Test handling of corrupted data during retrieval."""def test_invalid_key_handling(self) -> None:
"""Test handling of invalid key formats."""
def test_empty_key_handling(self) -> None:
"""Test handling of empty or null keys."""Key-value store tests include performance benchmarks:
def test_get_latency(self) -> None:
"""Benchmark get operation latency."""
def test_set_latency(self) -> None:
"""Benchmark set operation latency."""
def test_delete_latency(self) -> None:
"""Benchmark delete operation latency."""def test_bulk_operation_throughput(self) -> None:
"""Benchmark bulk operation throughput."""
def test_concurrent_access_performance(self) -> None:
"""Test performance under concurrent access."""Tests for memory-efficient implementations:
def test_memory_usage_patterns(self) -> None:
"""Test memory usage with large datasets."""
def test_garbage_collection_behavior(self) -> None:
"""Test proper cleanup of resources."""For thread-safe implementations:
def test_concurrent_reads(self) -> None:
"""Test concurrent read operations."""
def test_concurrent_writes(self) -> None:
"""Test concurrent write operations."""
def test_read_write_consistency(self) -> None:
"""Test consistency between concurrent reads and writes."""def test_lock_free_operations(self) -> None:
"""Test lock-free operation patterns."""
def test_atomic_operations(self) -> None:
"""Test atomic read-modify-write operations."""For persistent store implementations:
def test_data_persistence(self) -> None:
"""Test that data survives store restart."""
def test_crash_recovery(self) -> None:
"""Test recovery from unexpected shutdown."""def test_backup_functionality(self) -> None:
"""Test data backup capabilities."""
def test_restore_functionality(self) -> None:
"""Test data restore capabilities."""For multi-tenant implementations:
def test_namespace_isolation(self) -> None:
"""Test that different namespaces are isolated."""
def test_namespace_cleanup(self) -> None:
"""Test proper cleanup of namespace data."""Validate that stores respect configuration parameters:
def test_connection_string_parsing(self) -> None:
"""Test parsing of connection strings."""
def test_connection_pool_management(self) -> None:
"""Test connection pool behavior."""def test_json_serialization(self) -> None:
"""Test JSON serialization of values."""
def test_pickle_serialization(self) -> None:
"""Test pickle serialization of Python objects."""
def test_custom_serialization(self) -> None:
"""Test custom serialization schemes."""The key-value store testing framework provides comprehensive validation of storage implementations, ensuring they meet LangChain's requirements for reliability, performance, and consistency across both synchronous and asynchronous usage patterns.
Install with Tessl CLI
npx tessl i tessl/pypi-langchain-tests