167 lines
4.4 KiB
Python
167 lines
4.4 KiB
Python
import inspect
|
|
import os
|
|
import re
|
|
import unittest
|
|
from unittest.mock import patch
|
|
|
|
os.environ.setdefault("DATABASE_URL", "sqlite:///:memory:")
|
|
|
|
from scripts import run_migration_static_checks
|
|
from src.db.base import Base
|
|
|
|
|
|
TRAINING_REQUIRED_SCHEMA = {
|
|
"training_cases": (
|
|
"id",
|
|
"case_type",
|
|
"skill_tag",
|
|
"instrument_id",
|
|
"timeframe",
|
|
"start_ts",
|
|
"decision_ts",
|
|
"end_ts",
|
|
"source_event_refs",
|
|
"answer_key",
|
|
"status",
|
|
"created_at",
|
|
"updated_at",
|
|
),
|
|
"training_case_tasks": (
|
|
"id",
|
|
"case_id",
|
|
"task_type",
|
|
"skill_tag",
|
|
"prompt",
|
|
"required_annotation_types",
|
|
"max_score",
|
|
"status",
|
|
"created_at",
|
|
"updated_at",
|
|
),
|
|
"training_attempts": (
|
|
"id",
|
|
"case_id",
|
|
"user_id",
|
|
"status",
|
|
"started_at",
|
|
"submitted_at",
|
|
"total_score",
|
|
"created_at",
|
|
"updated_at",
|
|
),
|
|
"training_annotations": (
|
|
"id",
|
|
"attempt_id",
|
|
"user_id",
|
|
"annotation_type",
|
|
"payload",
|
|
"created_at",
|
|
"updated_at",
|
|
),
|
|
"training_scores": (
|
|
"id",
|
|
"attempt_id",
|
|
"user_id",
|
|
"category",
|
|
"score",
|
|
"max_score",
|
|
"feedback",
|
|
"created_at",
|
|
"updated_at",
|
|
),
|
|
"training_progress": (
|
|
"id",
|
|
"user_id",
|
|
"skill_tag",
|
|
"attempt_count",
|
|
"avg_score",
|
|
"last_attempt_at",
|
|
"weakness_tags",
|
|
"created_at",
|
|
"updated_at",
|
|
),
|
|
}
|
|
|
|
|
|
class _Result:
|
|
def __init__(self, rows=None, scalar=1):
|
|
self._rows = rows or []
|
|
self._scalar = scalar
|
|
|
|
def mappings(self):
|
|
return self
|
|
|
|
def all(self):
|
|
return self._rows
|
|
|
|
def first(self):
|
|
return self._rows[0] if self._rows else None
|
|
|
|
def scalar_one(self):
|
|
return self._scalar
|
|
|
|
|
|
class _ReadSession:
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc, traceback):
|
|
return False
|
|
|
|
def execute(self, sql, params=None):
|
|
sql_text = str(sql).lower()
|
|
case_row = {
|
|
"id": 7,
|
|
"case_type": "sweep",
|
|
"skill_tag": "liquidity_pool",
|
|
"instrument_id": 1,
|
|
"timeframe": "15m",
|
|
"start_ts": "2026-04-01T00:00:00Z",
|
|
"decision_ts": "2026-04-01T02:00:00Z",
|
|
"end_ts": "2026-04-01T04:00:00Z",
|
|
"source_event_refs": [{"table": "sweep_events", "id": 5}],
|
|
"status": "published",
|
|
"created_at": "2026-04-01T00:00:00Z",
|
|
"updated_at": "2026-04-01T00:00:00Z",
|
|
}
|
|
if "answer_key" in sql_text:
|
|
case_row["answer_key"] = {"side": "buy_side"}
|
|
return _Result(rows=[case_row])
|
|
|
|
|
|
class TrainingCaseEngineM1Tests(unittest.TestCase):
|
|
def test_migration_static_checks_can_identify_training_tables(self) -> None:
|
|
result = run_migration_static_checks.run_migration_static_checks(required_schema=TRAINING_REQUIRED_SCHEMA)
|
|
|
|
self.assertTrue(result["passed"], result["errors"])
|
|
|
|
def test_model_metadata_contains_training_tables(self) -> None:
|
|
import src.models # noqa: F401
|
|
|
|
for table_name in TRAINING_REQUIRED_SCHEMA:
|
|
self.assertIn(table_name, Base.metadata.tables)
|
|
|
|
def test_repository_source_does_not_write_to_trading_chain_tables(self) -> None:
|
|
from src.repositories.training_case_repository import TrainingCaseRepository
|
|
|
|
source = inspect.getsource(TrainingCaseRepository).lower()
|
|
|
|
for table_name in ("trade_signals", "trade_executions", "backtest_results"):
|
|
forbidden = re.compile(rf"\b(insert\s+into|update|delete\s+from|truncate\s+table)\s+{table_name}\b")
|
|
self.assertIsNone(forbidden.search(source), f"repository writes to {table_name}")
|
|
|
|
def test_list_cases_does_not_expose_answer_key_by_default(self) -> None:
|
|
from src.repositories import training_case_repository as repository_module
|
|
|
|
repository = repository_module.TrainingCaseRepository()
|
|
|
|
with patch.object(repository_module, "SessionLocal", return_value=_ReadSession()):
|
|
cases = repository.list_cases()
|
|
|
|
self.assertEqual(len(cases), 1)
|
|
self.assertNotIn("answer_key", cases[0])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|