mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-13 18:15:02 +00:00
chore: more tests
This commit is contained in:
1 parent
dc6db847ca
commit
d5c0322e2a
64 files changed
+24436
-140
No files matched your search
+299
-1
@@ -2,7 +2,12 @@ import json
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from application.cache import gen_cache, gen_cache_key, stream_cache
|
||||
from application.cache import (
|
||||
gen_cache,
|
||||
gen_cache_key,
|
||||
get_redis_instance,
|
||||
stream_cache,
|
||||
)
|
||||
from application.utils import get_hash
|
||||
|
||||
|
||||
@@ -120,3 +125,296 @@ def test_stream_cache_miss(mock_make_redis):
|
||||
assert result == ["new_chunk"]
|
||||
mock_redis_instance.get.assert_called_once()
|
||||
mock_redis_instance.set.assert_called_once()
|
||||
|
||||
|
||||
# ── get_redis_instance ──────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestGetRedisInstance:
|
||||
|
||||
def setup_method(self):
|
||||
"""Reset module-level redis state between tests."""
|
||||
import application.cache as cache_mod
|
||||
|
||||
cache_mod._redis_instance = None
|
||||
cache_mod._redis_creation_failed = False
|
||||
|
||||
def teardown_method(self):
|
||||
import application.cache as cache_mod
|
||||
|
||||
cache_mod._redis_instance = None
|
||||
cache_mod._redis_creation_failed = False
|
||||
|
||||
@patch("application.cache.redis.Redis.from_url")
|
||||
@patch("application.cache.settings")
|
||||
def test_creates_redis_instance(self, mock_settings, mock_from_url):
|
||||
mock_settings.CACHE_REDIS_URL = "redis://localhost:6379/0"
|
||||
mock_instance = MagicMock()
|
||||
mock_from_url.return_value = mock_instance
|
||||
|
||||
result = get_redis_instance()
|
||||
|
||||
assert result is mock_instance
|
||||
mock_from_url.assert_called_once_with(
|
||||
"redis://localhost:6379/0", socket_connect_timeout=2
|
||||
)
|
||||
|
||||
@patch("application.cache.redis.Redis.from_url")
|
||||
@patch("application.cache.settings")
|
||||
def test_returns_cached_instance(self, mock_settings, mock_from_url):
|
||||
mock_settings.CACHE_REDIS_URL = "redis://localhost:6379/0"
|
||||
mock_instance = MagicMock()
|
||||
mock_from_url.return_value = mock_instance
|
||||
|
||||
result1 = get_redis_instance()
|
||||
result2 = get_redis_instance()
|
||||
|
||||
assert result1 is result2
|
||||
assert mock_from_url.call_count == 1
|
||||
|
||||
@patch("application.cache.redis.Redis.from_url")
|
||||
@patch("application.cache.settings")
|
||||
def test_value_error_stops_retries(self, mock_settings, mock_from_url):
|
||||
import application.cache as cache_mod
|
||||
|
||||
mock_settings.CACHE_REDIS_URL = "invalid://url"
|
||||
mock_from_url.side_effect = ValueError("Invalid Redis URL")
|
||||
|
||||
result = get_redis_instance()
|
||||
|
||||
assert result is None
|
||||
assert cache_mod._redis_creation_failed is True
|
||||
|
||||
# Subsequent calls should not retry
|
||||
mock_from_url.reset_mock()
|
||||
result2 = get_redis_instance()
|
||||
assert result2 is None
|
||||
mock_from_url.assert_not_called()
|
||||
|
||||
@patch("application.cache.redis.Redis.from_url")
|
||||
@patch("application.cache.settings")
|
||||
def test_connection_error_allows_retries(self, mock_settings, mock_from_url):
|
||||
import application.cache as cache_mod
|
||||
import redis as redis_mod
|
||||
|
||||
mock_settings.CACHE_REDIS_URL = "redis://unreachable:6379/0"
|
||||
mock_from_url.side_effect = redis_mod.ConnectionError("Connection refused")
|
||||
|
||||
result = get_redis_instance()
|
||||
|
||||
assert result is None
|
||||
assert cache_mod._redis_creation_failed is False
|
||||
|
||||
# Subsequent calls should retry
|
||||
mock_from_url.side_effect = None
|
||||
mock_from_url.return_value = MagicMock()
|
||||
result2 = get_redis_instance()
|
||||
assert result2 is not None
|
||||
|
||||
|
||||
# ── gen_cache_key edge cases ────────────────────────────────────────────────
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_gen_cache_key_with_tools():
|
||||
messages = [{"role": "user", "content": "test"}]
|
||||
tools = [{"type": "function", "function": {"name": "test"}}]
|
||||
|
||||
key = gen_cache_key(messages, model="docgpt", tools=tools)
|
||||
assert isinstance(key, str)
|
||||
assert len(key) == 32
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_gen_cache_key_default_model():
|
||||
messages = [{"role": "user", "content": "test"}]
|
||||
key = gen_cache_key(messages)
|
||||
assert isinstance(key, str)
|
||||
assert len(key) == 32
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_gen_cache_key_deterministic():
|
||||
messages = [{"role": "user", "content": "test"}]
|
||||
key1 = gen_cache_key(messages, model="m1")
|
||||
key2 = gen_cache_key(messages, model="m1")
|
||||
assert key1 == key2
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_gen_cache_key_different_models():
|
||||
messages = [{"role": "user", "content": "test"}]
|
||||
key1 = gen_cache_key(messages, model="m1")
|
||||
key2 = gen_cache_key(messages, model="m2")
|
||||
assert key1 != key2
|
||||
|
||||
|
||||
# ── gen_cache with tools bypass ─────────────────────────────────────────────
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("application.cache.get_redis_instance")
|
||||
def test_gen_cache_bypasses_when_tools_provided(mock_make_redis):
|
||||
"""When tools are provided, caching is bypassed."""
|
||||
mock_redis_instance = MagicMock()
|
||||
mock_make_redis.return_value = mock_redis_instance
|
||||
|
||||
@gen_cache
|
||||
def mock_function(self, model, messages, stream, tools):
|
||||
return "direct_result"
|
||||
|
||||
messages = [{"role": "user", "content": "test"}]
|
||||
tools = [{"type": "function"}]
|
||||
result = mock_function(None, "model", messages, stream=False, tools=tools)
|
||||
|
||||
assert result == "direct_result"
|
||||
mock_redis_instance.get.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("application.cache.get_redis_instance")
|
||||
def test_gen_cache_no_redis(mock_make_redis):
|
||||
"""When redis is unavailable, function runs without caching."""
|
||||
mock_make_redis.return_value = None
|
||||
|
||||
@gen_cache
|
||||
def mock_function(self, model, messages, stream, tools):
|
||||
return "no_cache_result"
|
||||
|
||||
messages = [{"role": "user", "content": "test"}]
|
||||
result = mock_function(None, "model", messages, stream=False, tools=None)
|
||||
|
||||
assert result == "no_cache_result"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("application.cache.get_redis_instance")
|
||||
def test_gen_cache_redis_get_error(mock_make_redis):
|
||||
"""When redis.get raises, function falls through gracefully."""
|
||||
mock_redis_instance = MagicMock()
|
||||
mock_make_redis.return_value = mock_redis_instance
|
||||
mock_redis_instance.get.side_effect = Exception("Redis error")
|
||||
|
||||
@gen_cache
|
||||
def mock_function(self, model, messages, stream, tools):
|
||||
return "fallback_result"
|
||||
|
||||
messages = [{"role": "user", "content": "test"}]
|
||||
result = mock_function(None, "model", messages, stream=False, tools=None)
|
||||
|
||||
assert result == "fallback_result"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("application.cache.get_redis_instance")
|
||||
def test_gen_cache_redis_set_error(mock_make_redis):
|
||||
"""When redis.set raises, the result is still returned."""
|
||||
mock_redis_instance = MagicMock()
|
||||
mock_make_redis.return_value = mock_redis_instance
|
||||
mock_redis_instance.get.return_value = None
|
||||
mock_redis_instance.set.side_effect = Exception("Redis write error")
|
||||
|
||||
@gen_cache
|
||||
def mock_function(self, model, messages, stream, tools):
|
||||
return "result_str"
|
||||
|
||||
messages = [{"role": "user", "content": "test"}]
|
||||
result = mock_function(None, "model", messages, stream=False, tools=None)
|
||||
|
||||
assert result == "result_str"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("application.cache.get_redis_instance")
|
||||
def test_gen_cache_non_string_result_not_cached(mock_make_redis):
|
||||
"""Non-string results should not be cached."""
|
||||
mock_redis_instance = MagicMock()
|
||||
mock_make_redis.return_value = mock_redis_instance
|
||||
mock_redis_instance.get.return_value = None
|
||||
|
||||
@gen_cache
|
||||
def mock_function(self, model, messages, stream, tools):
|
||||
return {"key": "value"} # not a string
|
||||
|
||||
messages = [{"role": "user", "content": "test"}]
|
||||
result = mock_function(None, "model", messages, stream=False, tools=None)
|
||||
|
||||
assert result == {"key": "value"}
|
||||
mock_redis_instance.set.assert_not_called()
|
||||
|
||||
|
||||
# ── stream_cache edge cases ─────────────────────────────────────────────────
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("application.cache.get_redis_instance")
|
||||
def test_stream_cache_bypasses_when_tools_provided(mock_make_redis):
|
||||
"""When tools are provided, streaming cache is bypassed."""
|
||||
mock_redis_instance = MagicMock()
|
||||
mock_make_redis.return_value = mock_redis_instance
|
||||
|
||||
@stream_cache
|
||||
def mock_function(self, model, messages, stream, tools):
|
||||
yield "direct_chunk"
|
||||
|
||||
messages = [{"role": "user", "content": "test"}]
|
||||
tools = [{"type": "function"}]
|
||||
result = list(mock_function(None, "model", messages, stream=True, tools=tools))
|
||||
|
||||
assert result == ["direct_chunk"]
|
||||
mock_redis_instance.get.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("application.cache.get_redis_instance")
|
||||
def test_stream_cache_no_redis(mock_make_redis):
|
||||
"""When redis is unavailable, streaming works without caching."""
|
||||
mock_make_redis.return_value = None
|
||||
|
||||
@stream_cache
|
||||
def mock_function(self, model, messages, stream, tools):
|
||||
yield "chunk1"
|
||||
yield "chunk2"
|
||||
|
||||
messages = [{"role": "user", "content": "test"}]
|
||||
result = list(mock_function(None, "model", messages, stream=True, tools=None))
|
||||
|
||||
assert result == ["chunk1", "chunk2"]
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("application.cache.get_redis_instance")
|
||||
def test_stream_cache_redis_get_error(mock_make_redis):
|
||||
"""When redis.get raises during stream, falls through gracefully."""
|
||||
mock_redis_instance = MagicMock()
|
||||
mock_make_redis.return_value = mock_redis_instance
|
||||
mock_redis_instance.get.side_effect = Exception("Redis error")
|
||||
|
||||
@stream_cache
|
||||
def mock_function(self, model, messages, stream, tools):
|
||||
yield "fallback_chunk"
|
||||
|
||||
messages = [{"role": "user", "content": "test"}]
|
||||
result = list(mock_function(None, "model", messages, stream=True, tools=None))
|
||||
|
||||
assert result == ["fallback_chunk"]
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("application.cache.get_redis_instance")
|
||||
def test_stream_cache_redis_set_error(mock_make_redis):
|
||||
"""When redis.set raises during stream save, chunks are still yielded."""
|
||||
mock_redis_instance = MagicMock()
|
||||
mock_make_redis.return_value = mock_redis_instance
|
||||
mock_redis_instance.get.return_value = None
|
||||
mock_redis_instance.set.side_effect = Exception("Redis write error")
|
||||
|
||||
@stream_cache
|
||||
def mock_function(self, model, messages, stream, tools):
|
||||
yield "chunk"
|
||||
|
||||
messages = [{"role": "user", "content": "test"}]
|
||||
result = list(mock_function(None, "model", messages, stream=True, tools=None))
|
||||
|
||||
assert result == ["chunk"]
|
||||
Reference in new issue
Block a user