CoolFace
Apppublic

Hariprita/nl2sql-openenv

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes
test_local.py239 linesDownload Raw Back to root
1"""2test_local.py — Integration tests for the SQL Agent Environment.3 4Starts the FastAPI server as a subprocess, exercises every endpoint,5validates rewards, and prints ALL TESTS PASSED on success.6"""7 8import json9import subprocess10import sys11import time12import os13 14# Force UTF-8 output on Windows so Unicode characters in feedback don't crash prints15if sys.platform == "win32":16    import io17    sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")18    sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace")19 20import requests21 22BASE_URL = "http://localhost:7860"23SERVER_PROC = None24 25 26# ---------------------------------------------------------------------------27# Server lifecycle28# ---------------------------------------------------------------------------29 30def start_server():31    global SERVER_PROC32    env = os.environ.copy()33    SERVER_PROC = subprocess.Popen(34        [sys.executable, "-m", "uvicorn", "server.app:app",35         "--host", "0.0.0.0", "--port", "7860"],36        stdout=subprocess.PIPE,37        stderr=subprocess.PIPE,38        env=env,39    )40    # Wait up to 30 s for the server to be ready41    for _ in range(30):42        try:43            r = requests.get(f"{BASE_URL}/health", timeout=2)44            if r.status_code == 200:45                print(f"[OK] Server ready (pid={SERVER_PROC.pid})")46                return47        except requests.exceptions.ConnectionError:48            pass49        time.sleep(1)50    stop_server()51    raise RuntimeError("Server did not start within 30 s.")52 53 54def stop_server():55    if SERVER_PROC is not None:56        SERVER_PROC.terminate()57        try:58            SERVER_PROC.wait(timeout=5)59        except subprocess.TimeoutExpired:60            SERVER_PROC.kill()61        print("[OK] Server stopped.")62 63 64# ---------------------------------------------------------------------------65# Helpers66# ---------------------------------------------------------------------------67 68def check(condition: bool, label: str):69    if condition:70        print(f"  [PASS] {label}")71    else:72        print(f"  [FAIL] {label}")73        stop_server()74        sys.exit(1)75 76 77# ---------------------------------------------------------------------------78# Tests79# ---------------------------------------------------------------------------80 81def test_health():82    print("\n--- /health ---")83    r = requests.get(f"{BASE_URL}/health", timeout=5)84    check(r.status_code == 200, "HTTP 200")85    data = r.json()86    check(data.get("status") == "ok", f"status==ok  (got {data})")87 88 89def test_tasks():90    print("\n--- GET /tasks ---")91    r = requests.get(f"{BASE_URL}/tasks", timeout=5)92    check(r.status_code == 200, "HTTP 200")93    data = r.json()94    tasks = data.get("tasks", [])95    check(len(tasks) == 3, f"3 tasks defined (got {len(tasks)})")96    ids = [t["id"] for t in tasks]97    check("simple_select" in ids, "simple_select task present")98    check("join_aggregation" in ids, "join_aggregation task present")99    check("window_ranking" in ids, "window_ranking task present")100 101 102def test_reset():103    print("\n--- POST /reset ---")104    r = requests.post(f"{BASE_URL}/reset", json={}, timeout=5)105    check(r.status_code == 200, "HTTP 200")106    data = r.json()107    print(f"  question  : {data.get('question','')[:80]}")108    check("schema" in data,    "response has 'schema'")109    check("question" in data,  "response has 'question'")110    check("task_id" in data,   "response has 'task_id'")111    check(data.get("done") is False, "done==False on reset")112    check(data.get("reward") == 0.0, "reward==0.0 on reset")113    return data114 115 116def test_step_correct(obs_data: dict):117    print("\n--- POST /step  (correct query — task 1) ---")118    sql = (119        "SELECT name, city FROM customers "120        "WHERE country = 'United States' "121        "ORDER BY name"122    )123    r = requests.post(f"{BASE_URL}/step", json={"sql_query": sql}, timeout=5)124    check(r.status_code == 200, "HTTP 200")125    data = r.json()126    reward = data.get("reward", -1)127    print(f"  reward    : {reward}")128    print(f"  feedback  : {data.get('feedback','')[:100]}")129    print(f"  result    : {str(data.get('result',''))[:120]}")130    check(reward >= 0.7, f"reward >= 0.7 for correct US customers query (got {reward})")131    return data132 133 134def test_step_bad_query():135    print("\n--- POST /step  (non-existent table — should give 0.0) ---")136    # Reset first so we are on task 1137    requests.post(f"{BASE_URL}/reset", json={}, timeout=5)138    r = requests.post(139        f"{BASE_URL}/step",140        json={"sql_query": "SELECT * FROM nonexistent_table"},141        timeout=5,142    )143    check(r.status_code == 200, "HTTP 200")144    data = r.json()145    reward = data.get("reward", -1)146    print(f"  reward    : {reward}")147    print(f"  feedback  : {data.get('feedback','')[:100]}")148    check(reward == 0.0, f"reward==0.0 for bad table name (got {reward})")149 150 151def test_step_forbidden():152    print("\n--- POST /step  (DROP TABLE — should give 0.0) ---")153    requests.post(f"{BASE_URL}/reset", json={}, timeout=5)154    r = requests.post(155        f"{BASE_URL}/step",156        json={"sql_query": "DROP TABLE customers"},157        timeout=5,158    )159    check(r.status_code == 200, "HTTP 200")160    data = r.json()161    reward = data.get("reward", -1)162    print(f"  reward    : {reward}")163    check(reward == 0.0, f"reward==0.0 for DROP statement (got {reward})")164 165 166def test_state():167    print("\n--- GET /state ---")168    r = requests.get(f"{BASE_URL}/state", timeout=5)169    check(r.status_code == 200, "HTTP 200")170    data = r.json()171    print(f"  state     : {json.dumps(data)}")172    check("episode_id" in data,        "state has episode_id")173    check("step_count" in data,        "state has step_count")174    check("cumulative_reward" in data, "state has cumulative_reward")175    check("done" in data,              "state has done")176 177 178def test_full_episode():179    print("\n--- Full episode walkthrough (3 tasks) ---")180    requests.post(f"{BASE_URL}/reset", json={}, timeout=5)181 182    queries = [183        # Task 1 — easy184        "SELECT name, city FROM customers WHERE country = 'United States' ORDER BY name",185        # Task 2 — medium186        (187            "SELECT p.category, SUM(oi.quantity * oi.unit_price) AS total_revenue "188            "FROM order_items oi "189            "JOIN products p ON oi.product_id = p.product_id "190            "GROUP BY p.category "191            "ORDER BY total_revenue DESC"192        ),193        # Task 3 — hard (window function with SQLite RANK())194        (195            "SELECT c.name, MAX(o.order_date) AS most_recent_order, "196            "RANK() OVER (ORDER BY SUM(o.total_amount) DESC) AS rank "197            "FROM customers c "198            "JOIN orders o ON c.customer_id = o.customer_id "199            "GROUP BY c.customer_id, c.name "200            "ORDER BY rank"201        ),202    ]203 204    total_reward = 0.0205    for i, sql in enumerate(queries, 1):206        r = requests.post(f"{BASE_URL}/step", json={"sql_query": sql}, timeout=5)207        check(r.status_code == 200, f"task {i}: HTTP 200")208        data = r.json()209        reward = data.get("reward", 0.0)210        total_reward += reward211        print(f"  Task {i} reward: {reward:.2f} | feedback: {data.get('feedback','')[:80]}")212 213    check(total_reward > 0, f"episode produced non-zero reward (got {total_reward:.2f})")214    print(f"  Total episode reward: {total_reward:.2f}")215 216 217# ---------------------------------------------------------------------------218# Entry point219# ---------------------------------------------------------------------------220 221if __name__ == "__main__":222    print("Starting SQL Agent Environment tests...")223    start_server()224    try:225        test_health()226        test_tasks()227        obs = test_reset()228        test_step_correct(obs)229        test_step_bad_query()230        test_step_forbidden()231        test_state()232        test_full_episode()233    finally:234        stop_server()235 236    print("\n" + "=" * 40)237    print("ALL TESTS PASSED")238    print("=" * 40)239