"""DB reset tool tests (dcy-020 / REQ-064, BAT-030 M4). PostgreSQL-gated (TREX_POSTGRESQL_TEST_DSN) — the wipe-and-reinitialize tool that replaced the data-preserving cutover: wipes the slot-core tables and reapplies migrations so the DB lands on the clean asset-centric schema. """ from __future__ import annotations from trex_core.adapters.persistence.postgresql import PostgreSQLSlotRepository from trex_core.domain.slot import EXPECTED_SLOT_FIELDS, SlotService, SlotState from trex_core.runtime.reset import SLOT_CORE_TABLES, run_reset INST = "default" def _seed(dsn): """Create asset slots + drive one into holding so there are rows to wipe.""" repo = PostgreSQLSlotRepository(dsn) svc = SlotService(repo, instance_key=INST) repo.reconcile_universe(INST, {"pf-a": "SYM", "pf-b": "SYM"}) s = repo.find_slot_by_portfolio(INST, "pf-a") svc.transit_slot(s.slot_id, SlotState.INACTIVE, SlotState.PENDING_BUY, reason="r", actor="t", symbol="005930", qty=10) return repo def test_reset_wipes_and_lands_asset_centric(pg_clean): repo = _seed(pg_clean) assert len(repo.list_slots(INST)) == 2 report = run_reset(pg_clean, dry_run=False) assert report.dry_run is False assert report.schema_ok is True assert report.wiped["daily_slot"] == 2 assert repo.list_slots(INST) == [] # wiped # schema is the clean asset-centric form import psycopg with psycopg.connect(pg_clean) as c, c.cursor() as cur: cur.execute("SELECT column_name FROM information_schema.columns " "WHERE table_name='daily_slot'") cols = {r[0] for r in cur.fetchall()} assert cols == set(EXPECTED_SLOT_FIELDS) cur.execute("SELECT pg_get_constraintdef(oid) FROM pg_constraint " "WHERE conname='daily_slot_status_check'") assert "inactive" in cur.fetchone()[0] cur.execute("SELECT indexname FROM pg_indexes WHERE tablename='daily_slot'") assert "uq_daily_slot_instance_portfolio" in {r[0] for r in cur.fetchall()} def test_dry_run_reports_without_wiping(pg_clean): repo = _seed(pg_clean) report = run_reset(pg_clean, dry_run=True) assert report.dry_run is True assert report.wiped["daily_slot"] == 2 # reported assert len(repo.list_slots(INST)) == 2 # but NOT wiped def test_reset_is_idempotent(pg_clean): _seed(pg_clean) run_reset(pg_clean, dry_run=False) report2 = run_reset(pg_clean, dry_run=False) # second pass on clean DB assert report2.schema_ok is True assert report2.wiped["daily_slot"] == 0 def test_reset_usable_for_admission_after_wipe(pg_clean): """After reset the DB is immediately usable: reconcile + activate work.""" _seed(pg_clean) run_reset(pg_clean, dry_run=False) repo = PostgreSQLSlotRepository(pg_clean) svc = SlotService(repo, instance_key=INST) repo.reconcile_universe(INST, {"pf-x": "SYM"}) s = repo.find_slot_by_portfolio(INST, "pf-x") assert s.status is SlotState.INACTIVE svc.transit_slot(s.slot_id, SlotState.INACTIVE, SlotState.PENDING_BUY, reason="r", actor="t", symbol="005930") assert repo.count_active(INST) == 1 def test_covers_all_slot_core_tables(pg_clean): """Sanity: the tool targets every slot-core table the migrations create.""" import psycopg with psycopg.connect(pg_clean) as c, c.cursor() as cur: cur.execute("SELECT table_name FROM information_schema.tables " "WHERE table_schema='public' AND table_name = ANY(%s)", (list(SLOT_CORE_TABLES),)) present = {r[0] for r in cur.fetchall()} assert present == set(SLOT_CORE_TABLES)