2026-09-09 18:04:45 +08:00
|
|
|
"""cache_service 缓存单元测试(内存后端,无需 Redis)。"""
|
|
|
|
|
import time
|
|
|
|
|
import unittest
|
|
|
|
|
|
|
|
|
|
from app.service.cache_service import CacheService, InMemoryBackend
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestCacheService(unittest.TestCase):
|
|
|
|
|
def setUp(self):
|
|
|
|
|
self.svc = CacheService(backend=InMemoryBackend())
|
|
|
|
|
|
|
|
|
|
def test_sql_hash_deterministic(self):
|
|
|
|
|
self.assertEqual(self.svc.sql_hash("SELECT 1"), self.svc.sql_hash("SELECT 1"))
|
|
|
|
|
self.assertNotEqual(self.svc.sql_hash("SELECT 1"), self.svc.sql_hash("SELECT 2"))
|
|
|
|
|
|
|
|
|
|
def test_permission_fingerprint_differs_by_scope(self):
|
|
|
|
|
a = self.svc.permission_fingerprint("S1", "assigned", ["CUST-1"])
|
|
|
|
|
b = self.svc.permission_fingerprint("S1", "assigned", ["CUST-2"])
|
|
|
|
|
self.assertNotEqual(a, b)
|
|
|
|
|
|
|
|
|
|
def test_ttl_layering(self):
|
|
|
|
|
self.assertEqual(self.svc.ttl_for(["core_trade"]), 5 * 60)
|
|
|
|
|
self.assertEqual(self.svc.ttl_for(["risk_alert"]), 60 * 60)
|
|
|
|
|
self.assertEqual(self.svc.ttl_for(["core_customer"]), 24 * 60 * 60)
|
|
|
|
|
|
|
|
|
|
def test_roundtrip(self):
|
|
|
|
|
fp = self.svc.permission_fingerprint("S1", "full")
|
|
|
|
|
sql = "SELECT COUNT(*) FROM core_customer"
|
2026-09-10 15:22:07 +08:00
|
|
|
tables = ["core_customer"]
|
|
|
|
|
self.assertIsNone(self.svc.get_result(fp, sql, tables))
|
|
|
|
|
self.svc.set_result(fp, sql, {"cnt": 33}, tables)
|
|
|
|
|
self.assertEqual(self.svc.get_result(fp, sql, tables), {"cnt": 33})
|
2026-09-09 18:04:45 +08:00
|
|
|
|
|
|
|
|
def test_invalidate(self):
|
|
|
|
|
fp = self.svc.permission_fingerprint("S1", "full")
|
2026-09-10 15:22:07 +08:00
|
|
|
sql = "SELECT 1 FROM core_customer"
|
|
|
|
|
tables = ["core_customer"]
|
|
|
|
|
self.svc.set_result(fp, sql, {"x": 1}, tables)
|
|
|
|
|
self.svc.invalidate_by_sql(fp, sql, tables)
|
|
|
|
|
self.assertIsNone(self.svc.get_result(fp, sql, tables))
|
|
|
|
|
|
|
|
|
|
def test_invalidate_tables_bumps_generation(self):
|
|
|
|
|
fp = self.svc.permission_fingerprint("S1", "full")
|
|
|
|
|
sql = "SELECT COUNT(*) FROM core_trade"
|
|
|
|
|
tables = ["core_trade"]
|
|
|
|
|
self.svc.set_result(fp, sql, {"cnt": 1}, tables)
|
|
|
|
|
self.assertEqual(self.svc.get_result(fp, sql, tables), {"cnt": 1})
|
|
|
|
|
self.svc.invalidate_tables(["core_trade"])
|
|
|
|
|
self.assertIsNone(self.svc.get_result(fp, sql, tables))
|
|
|
|
|
self.svc.set_result(fp, sql, {"cnt": 2}, tables)
|
|
|
|
|
self.assertEqual(self.svc.get_result(fp, sql, tables), {"cnt": 2})
|
|
|
|
|
|
|
|
|
|
def test_invalidate_tables_skips_unrelated(self):
|
|
|
|
|
fp = self.svc.permission_fingerprint("S1", "full")
|
|
|
|
|
sql = "SELECT COUNT(*) FROM core_customer"
|
|
|
|
|
tables = ["core_customer"]
|
|
|
|
|
self.svc.set_result(fp, sql, {"cnt": 9}, tables)
|
|
|
|
|
self.svc.invalidate_tables(["core_trade"])
|
|
|
|
|
self.assertEqual(self.svc.get_result(fp, sql, tables), {"cnt": 9})
|
2026-09-09 18:04:45 +08:00
|
|
|
|
|
|
|
|
def test_inmemory_expiry(self):
|
|
|
|
|
b = InMemoryBackend()
|
|
|
|
|
b.set("k", "v", ttl=1)
|
|
|
|
|
self.assertEqual(b.get("k"), "v")
|
|
|
|
|
time.sleep(1.2)
|
|
|
|
|
self.assertIsNone(b.get("k"))
|
|
|
|
|
|
2026-09-10 15:22:07 +08:00
|
|
|
def test_bump_analyst_cache_helper(self):
|
|
|
|
|
from app.service import analyst_cache_invalidate
|
|
|
|
|
|
|
|
|
|
analyst_cache_invalidate._svc = self.svc
|
|
|
|
|
fp = self.svc.permission_fingerprint("S1", "full")
|
|
|
|
|
sql = "SELECT COUNT(*) FROM core_trade"
|
|
|
|
|
tables = ["core_trade"]
|
|
|
|
|
self.svc.set_result(fp, sql, {"cnt": 1}, tables)
|
|
|
|
|
analyst_cache_invalidate.bump_analyst_cache_for_trade()
|
|
|
|
|
self.assertIsNone(self.svc.get_result(fp, sql, tables))
|
|
|
|
|
|
2026-09-09 18:04:45 +08:00
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
unittest.main()
|