| 1 | from __future__ import annotations |
| 2 | |
| 3 | import re |
| 4 | from pathlib import Path |
| 5 | |
| 6 | |
| 7 | REPOSITORY_ROOT = Path(__file__).resolve().parents[3] |
| 8 | ENGINE_APP = REPOSITORY_ROOT / "ai" / "research-engine" / "app" |
| 9 | RESET_SQL = REPOSITORY_ROOT / "docs" / "architecture" / "04-safe-research-reset.sql" |
| 10 | V9_MIGRATION = ( |
| 11 | REPOSITORY_ROOT |
| 12 | / "services" |
| 13 | / "research-service" |
| 14 | / "src" |
| 15 | / "main" |
| 16 | / "resources" |
| 17 | / "db" |
| 18 | / "migration" |
| 19 | / "V9__remove_legacy_research_refresh_jobs.sql" |
| 20 | ) |
| 21 | |
| 22 | |
| 23 | def _executable_sql(path: Path) -> str: |
| 24 | return "\n".join( |
| 25 | line for line in path.read_text(encoding="utf-8").splitlines() |
| 26 | if not line.lstrip().startswith("--") |
| 27 | ) |
| 28 | |
| 29 | |
| 30 | def test_removed_legacy_orchestrators_have_no_production_import_or_table_caller() -> None: |
| 31 | sources = "\n".join( |
| 32 | path.read_text(encoding="utf-8") |
| 33 | for path in ENGINE_APP.glob("*.py") |
| 34 | ) |
| 35 | |
| 36 | assert not (ENGINE_APP / "refresh_jobs.py").exists() |
| 37 | assert not (ENGINE_APP / "market_intelligence_research.py").exists() |
| 38 | assert "ResearchRefreshJobManager" not in sources |
| 39 | assert "MarketIntelligenceResearchPrefetch" not in sources |
| 40 | assert "research_refresh_jobs" not in sources |
| 41 | |
| 42 | |
| 43 | def test_safe_reset_deletes_only_the_reviewed_rebuildable_allowlist() -> None: |
| 44 | sql = _executable_sql(RESET_SQL) |
| 45 | deleted_tables = re.findall( |
| 46 | r"DELETE\s+FROM\s+([a-z_]+\.[a-z0-9_]+)", sql, flags=re.IGNORECASE |
| 47 | ) |
| 48 | |
| 49 | assert set(deleted_tables) == { |
| 50 | "research.global_stock_rule_engine_results", |
| 51 | "research.research_event_sources", |
| 52 | "research.global_shareholding_snapshot_values", |
| 53 | "research.global_shareholding_snapshots", |
| 54 | "research.research_events", |
| 55 | "research.global_financial_facts", |
| 56 | "research.global_structured_market_snapshots", |
| 57 | "research.research_documents", |
| 58 | "research.research_refresh_jobs", |
| 59 | "research.research_refresh_runs", |
| 60 | } |
| 61 | assert len(deleted_tables) == 10 |
| 62 | assert not re.search(r"\bTRUNCATE\b", sql, flags=re.IGNORECASE) |
| 63 | assert not re.search(r"\bDROP\s+SCHEMA\b", sql, flags=re.IGNORECASE) |
| 64 | assert "DELETE FROM research.global_market_price_observations" not in sql |
| 65 | assert "DELETE FROM research.flyway_schema_history_research" not in sql |
| 66 | assert "DELETE FROM research.irfc_financial_facts_backup_20260902" not in sql |
| 67 | assert "DELETE FROM portfolio." not in sql |
| 68 | |
| 69 | |
| 70 | def test_v9_drops_only_the_dead_refresh_job_table() -> None: |
| 71 | sql = _executable_sql(V9_MIGRATION) |
| 72 | drops = re.findall( |
| 73 | r"DROP\s+TABLE\s+(?:IF\s+EXISTS\s+)?([a-z_]+\.[a-z0-9_]+)", |
| 74 | sql, |
| 75 | flags=re.IGNORECASE, |
| 76 | ) |
| 77 | |
| 78 | assert drops == ["research.research_refresh_jobs"] |
| 79 | assert "research_refresh_runs" not in sql |