You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
buffer_platform/tests/test_analysis_storage.py

152 lines
6.0 KiB

"""Analysis 存储迁移测试Redis 缓存 + MySQL/SQLite 降级。"""
import json
from datetime import datetime
from unittest.mock import MagicMock, patch
import pytest
from app.storage_manager import StorageManager
class TestStorageManagerCache:
"""StorageManager 通用缓存方法测试。"""
def test_cache_get_returns_redis_value_on_hit(self):
"""Redis 命中时返回反序列化数据。"""
redis_client = MagicMock()
redis_client.get.return_value = json.dumps({"symbol": "AG2606", "score": 85})
manager = StorageManager(redis_client=redis_client, mysql_engine=MagicMock())
result = manager.cache_get("analysis:ai_analysis_cache:1")
assert result == {"symbol": "AG2606", "score": 85}
redis_client.get.assert_called_once_with("analysis:ai_analysis_cache:1")
def test_cache_get_returns_none_when_redis_unavailable(self):
"""Redis 不可用时返回 None。"""
manager = StorageManager(redis_client=None, mysql_engine=MagicMock())
result = manager.cache_get("analysis:ai_analysis_cache:1")
assert result is None
def test_cache_get_returns_none_on_miss(self):
"""Redis 未命中时返回 None。"""
redis_client = MagicMock()
redis_client.get.return_value = None
manager = StorageManager(redis_client=redis_client, mysql_engine=MagicMock())
result = manager.cache_get("analysis:ai_analysis_cache:1")
assert result is None
def test_cache_set_writes_to_redis_with_ttl(self):
"""cache_set 使用 TTL 写入 Redis。"""
redis_client = MagicMock()
manager = StorageManager(redis_client=redis_client, mysql_engine=MagicMock())
value = {"symbol": "AG2606", "score": 85}
manager.cache_set("analysis:ai_analysis_cache:1", value, ttl=300)
redis_client.setex.assert_called_once_with(
"analysis:ai_analysis_cache:1", 300, json.dumps(value, ensure_ascii=False)
)
def test_cache_set_returns_false_when_redis_unavailable(self):
"""Redis 不可用时 cache_set 返回 False。"""
manager = StorageManager(redis_client=None, mysql_engine=MagicMock())
result = manager.cache_set("analysis:ai_analysis_cache:1", {"score": 85}, ttl=300)
assert result is False
def test_cache_delete_removes_redis_key(self):
"""cache_delete 删除 Redis 键。"""
redis_client = MagicMock()
manager = StorageManager(redis_client=redis_client, mysql_engine=MagicMock())
manager.cache_delete("analysis:ai_analysis_cache:1")
redis_client.delete.assert_called_once_with("analysis:ai_analysis_cache:1")
class TestAnalysisDbSwitching:
"""analysis_db 动态 Session 切换测试。"""
def test_get_analysis_db_returns_mysql_session_when_mysql_available(self):
"""MySQL 可用时返回 MySQL Session。"""
from app.analysis_db import get_analysis_db
mysql_engine = MagicMock()
mock_session = MagicMock()
mock_sessionmaker = MagicMock(return_value=mock_session)
mock_session.__enter__ = MagicMock(return_value=mock_session)
mock_session.__exit__ = MagicMock(return_value=False)
with patch("app.analysis_db.MySQLSessionLocal", mock_sessionmaker):
with patch("app.analysis_db.get_storage_manager") as mock_sm:
sm = MagicMock()
sm.check_mysql.return_value = True
sm.mysql_engine = mysql_engine
mock_sm.return_value = sm
db = next(get_analysis_db())
assert db is mock_session
def test_get_analysis_db_returns_sqlite_session_when_mysql_unavailable(self):
"""MySQL 不可用时返回 SQLite Session。"""
from app.analysis_db import get_analysis_db
sqlite_engine = MagicMock()
mock_session = MagicMock()
mock_sessionmaker = MagicMock(return_value=mock_session)
mock_session.__enter__ = MagicMock(return_value=mock_session)
mock_session.__exit__ = MagicMock(return_value=False)
with patch("app.analysis_db.AnalysisSessionLocal", mock_sessionmaker):
with patch("app.analysis_db.get_storage_manager") as mock_sm:
sm = MagicMock()
sm.check_mysql.return_value = False
mock_sm.return_value = sm
db = next(get_analysis_db())
assert db is mock_session
class TestAnalysisMigration:
"""Analysis 数据迁移测试。"""
def test_migrate_skips_when_mysql_has_data(self):
"""MySQL 已有数据时跳过迁移。"""
from app.analysis_migration import migrate_analysis_sqlite_to_mysql
with patch("app.analysis_migration.mysql_engine", MagicMock()):
with patch("app.analysis_migration._count_mysql_analysis_records") as mock_count:
mock_count.return_value = 10
result = migrate_analysis_sqlite_to_mysql()
assert result is True
mock_count.assert_called_once()
def test_migrate_moves_data_from_sqlite_to_mysql(self):
"""SQLite 数据正确迁移到 MySQL。"""
from app.analysis_migration import migrate_analysis_sqlite_to_mysql
with patch("app.analysis_migration.mysql_engine", MagicMock()):
with patch("app.analysis_migration._count_mysql_analysis_records") as mock_count:
mock_count.return_value = 0
with patch("app.analysis_migration._read_sqlite_analysis_data") as mock_read:
mock_read.return_value = {
"ai_analysis_cache": [{"id": 1, "symbol": "AG2606"}]
}
with patch("app.analysis_migration._write_mysql_analysis_data") as mock_write:
mock_write.return_value = True
result = migrate_analysis_sqlite_to_mysql()
assert result is True
mock_write.assert_called_once_with({"ai_analysis_cache": [{"id": 1, "symbol": "AG2606"}]})