Files
crewAI/lib/crewai/tests/utilities/test_cache_config.py
Matthias Howell c5a9a8da50 feat(valkey): shared cache config + ValkeyCache for A2A and file uploads
Extract duplicated Redis URL parsing into a shared cache_config utility.
Introduce ValkeyCache as a lightweight async key/value cache using
valkey-glide. Wire it into A2A task handling, agent card caching, and
file upload caching.

Part 1/4 of Valkey storage implementation.

fix: async-safe embeddings and resilient drain_writes

Add bytes→float validators on MemoryRecord and ItemState to handle
Valkey returning embeddings as raw bytes. Make embed_texts() safe when
called from an async context by using a thread pool. Improve
drain_writes() with per-save timeouts and error logging instead of
raising on failure.

Part 3/4 of Valkey storage implementation.

feat(valkey): ValkeyStorage vector memory backend

Add ValkeyStorage, a distributed StorageBackend implementation using
Valkey-GLIDE with Valkey Search for vector similarity. Wire it into
Memory as the 'valkey' storage option. Pin scrapegraph-py<2 to fix
unrelated upstream breakage.

Part 4/4 of Valkey storage implementation.

fix: use datetime.utcnow() for last_accessed consistency

MemoryRecord defaults use utcnow() for created_at and last_accessed.
Match that in ValkeyStorage.update_record() to avoid timezone
inconsistency in recency scoring.

feat(valkey): shared cache config + ValkeyCache for A2A and file uploads

Extract duplicated Redis URL parsing into a shared cache_config utility.
Introduce ValkeyCache as a lightweight async key/value cache using
valkey-glide. Wire it into A2A task handling, agent card caching, and
file upload caching.

Part 1/4 of Valkey storage implementation.

fix: handle non-numeric database path in cache URL parsing

Extract _parse_db_from_path() helper that catches ValueError for
paths like /mydb and defaults to 0 with a warning, instead of
crashing.

fix: async-safe embeddings and resilient drain_writes

Add bytes→float validators on MemoryRecord and ItemState to handle
Valkey returning embeddings as raw bytes. Make embed_texts() safe when
called from an async context by using a thread pool. Improve
drain_writes() with per-save timeouts and error logging instead of
raising on failure.

Part 3/4 of Valkey storage implementation.

fix: catch concurrent.futures.TimeoutError for Python 3.10 compat

In Python <3.11, concurrent.futures.TimeoutError is distinct from the
builtin TimeoutError. Catch both so the timeout warning path works
on all supported Python versions.
2026-05-13 11:00:36 -04:00

126 lines
4.1 KiB
Python

"""Tests for shared cache configuration helpers."""
from __future__ import annotations
import os
from unittest.mock import patch
import pytest
from crewai.utilities.cache_config import (
get_aiocache_config,
parse_cache_url,
use_valkey_cache,
)
class TestParseCacheUrl:
"""Tests for parse_cache_url()."""
def test_returns_none_when_no_env_vars(self) -> None:
with patch.dict(os.environ, {}, clear=True):
assert parse_cache_url() is None
def test_parses_valkey_url(self) -> None:
with patch.dict(
os.environ, {"VALKEY_URL": "redis://myhost:6380/2"}, clear=True
):
result = parse_cache_url()
assert result is not None
assert result["host"] == "myhost"
assert result["port"] == 6380
assert result["db"] == 2
assert result["password"] is None
def test_parses_redis_url(self) -> None:
with patch.dict(
os.environ, {"REDIS_URL": "redis://localhost:6379/0"}, clear=True
):
result = parse_cache_url()
assert result is not None
assert result["host"] == "localhost"
assert result["port"] == 6379
assert result["db"] == 0
def test_valkey_url_takes_priority_over_redis_url(self) -> None:
with patch.dict(
os.environ,
{
"VALKEY_URL": "redis://valkey-host:6380/1",
"REDIS_URL": "redis://redis-host:6379/0",
},
clear=True,
):
result = parse_cache_url()
assert result is not None
assert result["host"] == "valkey-host"
assert result["port"] == 6380
def test_parses_password(self) -> None:
with patch.dict(
os.environ,
{"VALKEY_URL": "redis://:s3cret@myhost:6379/0"},
clear=True,
):
result = parse_cache_url()
assert result is not None
assert result["password"] == "s3cret"
def test_defaults_for_minimal_url(self) -> None:
with patch.dict(
os.environ, {"VALKEY_URL": "redis://myhost"}, clear=True
):
result = parse_cache_url()
assert result is not None
assert result["host"] == "myhost"
assert result["port"] == 6379
assert result["db"] == 0
assert result["password"] is None
def test_non_numeric_db_path_defaults_to_zero(self) -> None:
with patch.dict(
os.environ, {"VALKEY_URL": "redis://myhost:6379/mydb"}, clear=True
):
result = parse_cache_url()
assert result is not None
assert result["db"] == 0
class TestGetAiocacheConfig:
"""Tests for get_aiocache_config()."""
def test_returns_memory_cache_when_no_url(self) -> None:
with patch.dict(os.environ, {}, clear=True):
config = get_aiocache_config()
assert config["default"]["cache"] == "aiocache.SimpleMemoryCache"
def test_returns_redis_cache_when_url_set(self) -> None:
with patch.dict(
os.environ, {"VALKEY_URL": "redis://myhost:6380/2"}, clear=True
):
config = get_aiocache_config()
assert config["default"]["cache"] == "aiocache.RedisCache"
assert config["default"]["endpoint"] == "myhost"
assert config["default"]["port"] == 6380
assert config["default"]["db"] == 2
class TestUseValkeyCache:
"""Tests for use_valkey_cache()."""
def test_returns_false_when_not_set(self) -> None:
with patch.dict(os.environ, {}, clear=True):
assert use_valkey_cache() is False
def test_returns_true_when_set(self) -> None:
with patch.dict(
os.environ, {"VALKEY_URL": "redis://localhost:6379"}, clear=True
):
assert use_valkey_cache() is True
def test_returns_false_when_only_redis_url_set(self) -> None:
with patch.dict(
os.environ, {"REDIS_URL": "redis://localhost:6379"}, clear=True
):
assert use_valkey_cache() is False