Fin-EvalOps-v2 / backend /scripts /test_persistence.py
appQQQ's picture
init: import all code from Fin-EvalOps (with cache-fix middleware)
bcee114 verified
Raw
History Blame Contribute Delete
6.83 kB
#!/usr/bin/env python3
"""Local smoke test for `app.persistence` — does not start the FastAPI app.
Runs three checks:
1) Configuration check: are HF_TOKEN / HF_NAMESPACE / HF_DATASET_REPO set?
2) `pull_db(force=True)` — downloads fin_evalops.db from the Dataset repo
to a temp path, verifies it's a valid SQLite file.
3) `push_db(reason='local-test', force=True)` — backs up the local DB and
uploads it; verifies the file ends up in the Dataset repo.
Usage:
# 1. Quick: skip push (just verify config + pull)
python backend/scripts/test_persistence.py --no-push
# 2. Full: pull + push (uses your real HF_TOKEN from env)
python backend/scripts/test_persistence.py
# 3. Custom repo
HF_DATASET_REPO=my-test-db python backend/scripts/test_persistence.py
Notes:
- This script MUTATES the Dataset repo (creates/updates a commit). Use a
throwaway `HF_DATASET_REPO` for the first run.
- Requires `huggingface-hub` (added to backend/requirements.txt).
"""
from __future__ import annotations
import argparse
import os
import shutil
import sqlite3
import sys
import tempfile
from pathlib import Path
# Make `app` importable regardless of where this script is run from
HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE.parent))
from app import persistence # noqa: E402
from app.config import settings # noqa: E402
# ---------------------------------------------------------------------------
def _print_status() -> None:
print("=" * 60)
print("Fin-EvalOps HF persistence — local smoke test")
print("=" * 60)
print(f" HF_TOKEN : {'set (' + settings.hf_token[:6] + '...)' if settings.hf_token else '<empty>'}")
print(f" HF_NAMESPACE : {settings.hf_namespace or '<empty>'}")
print(f" HF_DATASET_REPO : {settings.hf_dataset_repo or '<empty>'}")
print(f" HF_PUSH_INTERVAL : {settings.hf_push_interval}s")
print(f" local DB path : {settings.db_path_abs}")
print(f" local DB exists : {Path(settings.db_path_abs).exists()}")
if Path(settings.db_path_abs).exists():
print(f" local DB size : {Path(settings.db_path_abs).stat().st_size:,} bytes")
print()
def _check_config() -> bool:
missing = []
if not settings.hf_token:
missing.append("HF_TOKEN")
if not settings.hf_namespace:
missing.append("HF_NAMESPACE")
if not settings.hf_dataset_repo:
missing.append("HF_DATASET_REPO")
if missing:
print(f"❌ Missing required env vars: {', '.join(missing)}")
print(" Export them and retry, e.g.:")
print(" export HF_TOKEN=hf_xxx")
print(" export HF_NAMESPACE=yourname")
return False
return True
def _check_sqlite(path: Path) -> bool:
try:
con = sqlite3.connect(str(path))
cur = con.execute("SELECT name FROM sqlite_master WHERE type='table'")
tables = [r[0] for r in cur.fetchall()]
con.close()
print(f" valid SQLite, {len(tables)} tables: {', '.join(tables[:6])}{'...' if len(tables) > 6 else ''}")
return True
except Exception as exc:
print(f" ❌ not a valid SQLite file: {exc}")
return False
def _download_to_temp() -> Path | None:
"""Download via huggingface_hub to a temp path (bypasses local DB)."""
from huggingface_hub import hf_hub_download
tmp = Path(tempfile.mkdtemp(prefix="hf_pull_"))
try:
path = hf_hub_download(
repo_id=f"{settings.hf_namespace}/{settings.hf_dataset_repo}",
filename="fin_evalops.db",
repo_type="dataset",
token=settings.hf_token,
local_dir=str(tmp),
)
p = Path(path)
print(f" downloaded to {p} ({p.stat().st_size:,} bytes)")
return p
except Exception as exc:
print(f" download failed: {type(exc).__name__}: {exc}")
shutil.rmtree(tmp, ignore_errors=True)
return None
# ---------------------------------------------------------------------------
def main() -> int:
ap = argparse.ArgumentParser(description=__doc__.split("\n\n", 1)[0])
ap.add_argument("--no-push", action="store_true", help="skip the push step (pull only)")
ap.add_argument("--no-pull", action="store_true", help="skip the pull step (push only)")
args = ap.parse_args()
_print_status()
if not _check_config():
return 1
failures = 0
# ---- 1) pull ----
if not args.no_pull:
print("📥 Step 1/3: pull fin_evalops.db from HF Dataset repo")
# Use the helper that bypasses local-DB-exists guard (we want to test
# even if the local DB is already populated).
print(" testing raw huggingface_hub download…")
p = _download_to_temp()
if not p:
print(" (This is OK on first deploy — Dataset repo may not exist yet.)")
else:
_check_sqlite(p)
shutil.rmtree(p.parent, ignore_errors=True)
print()
# ---- 2) push via persistence.push_db ----
if not args.no_push:
if not Path(settings.db_path_abs).exists():
print("⚠️ Step 2/3: local DB missing; creating a minimal test DB first")
Path(settings.db_path_abs).parent.mkdir(parents=True, exist_ok=True)
con = sqlite3.connect(str(settings.db_path_abs))
con.execute("CREATE TABLE IF NOT EXISTS _smoke_test (ts TEXT, msg TEXT)")
con.execute("INSERT INTO _smoke_test VALUES (datetime('now'), 'local smoke test')")
con.commit()
con.close()
print(f" created {settings.db_path_abs} with a _smoke_test row")
else:
print(f" using existing local DB ({Path(settings.db_path_abs).stat().st_size:,} bytes)")
print("📤 Step 2/3: push_db(reason='local-test', force=True)")
ok = persistence.push_db(reason="local-test", force=True)
print(f" result: {'✅ ok' if ok else '❌ failed'}")
if not ok:
failures += 1
print()
# ---- 3) status ----
print("📊 Step 3/3: persistence_status snapshot")
from huggingface_hub import HfApi
try:
api = HfApi(token=settings.hf_token)
files = api.list_repo_files(
repo_id=f"{settings.hf_namespace}/{settings.hf_dataset_repo}",
repo_type="dataset",
)
print(f" files in {settings.hf_namespace}/{settings.hf_dataset_repo}: {files}")
except Exception as exc:
print(f" could not list files: {type(exc).__name__}: {exc}")
print()
print("=" * 60)
if failures:
print(f"❌ {failures} step(s) failed")
else:
print("✅ all steps passed")
print("=" * 60)
return 0 if failures == 0 else 1
if __name__ == "__main__":
sys.exit(main())