- Introduced `TemplateService` for managing SQL templates, allowing for parameterized queries based on user input. - Added functionality to automatically reload templates upon asset creation in `analyst.py`. - Enhanced `CacheService` to support table generation bumping, ensuring cache invalidation on data changes. - Updated `RiskRepository` and `GatewayRepository` to trigger cache invalidation for relevant operations. - Expanded `analyst_schemas.py` to include new fields for template tracking in response metadata. - Created seed SQL script for populating initial templates and added unit tests for template rendering logic. This update significantly improves the efficiency of query handling by leveraging SQL templates, reducing reliance on LLM for common queries.
82 lines
3.3 KiB
Python
82 lines
3.3 KiB
Python
"""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"
|
|
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})
|
|
|
|
def test_invalidate(self):
|
|
fp = self.svc.permission_fingerprint("S1", "full")
|
|
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})
|
|
|
|
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"))
|
|
|
|
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))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|