fly-trader / tests /conftest.py
bryceweiner's picture
release 5 (acbf4d30d3) (part 2)
411abf3 verified
Raw History Blame Contribute Delete
4.02 kB
"""Every test runs against fly_trader_test. DATABASE_URL is forced before any fly_trader import so
the refuse-production guard in fly_trader.db.connection can never see the real database."""
import os
os.environ["DATABASE_URL"] = "postgresql:///fly_trader_test"
os.environ["FLY_TRADER_TESTING"] = "1" # fly_trader/netguard.py: no transaction, order or transfer leaves a test run
for _name in ("BOT_PRIVATE_KEY", "BOT_PRIVATE_KEY_FILE", "RH_BOT_PRIVATE_KEY", "SIGNER_SOCKET"): # and the operator's signing keys
os.environ[_name] = "" # (or signer) never load (empty = unset; .env never overrides)
for _name in ("RH_RPC_URL_TX", "QUICKNODE_RH_API_URL", "QUICKNODE_RH_API_KEY"): # no test reaches the real transaction endpoint
os.environ[_name] = ""
for _name in ("LIVE_ENABLED", "RH_LIVE_ENABLED", "KALSHI_LIVE_ENABLED", "LIVE_WITHOUT_HANDOVER"): # nor runs with the operator's
os.environ[_name] = "0" # live switches from .env
os.environ["RH_CAPITAL_ETH"] = "" # empty = the code's pinned default, not the operator's wallet balance (load_dotenv never overrides)
import pytest # noqa: E402
TEST_URL = "postgresql:///fly_trader_test"
@pytest.fixture(scope="session", autouse=True)
def _test_database():
from fly_trader.db import schema
schema.ensure_database(TEST_URL)
schema.apply_schema(TEST_URL)
yield
@pytest.fixture
def db_conn():
from fly_trader.db.connection import connect
conn = connect(TEST_URL)
try:
yield conn
finally:
conn.rollback()
conn.close()
class FakeSupervisor:
"""The console's view of the Supervisor in tests: worker state you set, and every control call recorded."""
def __init__(self):
from fly_trader.ops.supervisor import WORKERS
self.alive_set: set[str] = set()
self.calls: list[tuple] = []
self.pid = os.getpid()
self.autostarted = True
self.workers = WORKERS
def status(self) -> dict:
return {n: {"alive": n in self.alive_set, "started_at": None, "stopped_at": None, "error": None, "stopping": False} for n in self.workers}
def alive(self, name: str) -> bool:
return name in self.alive_set
def external(self) -> dict:
return {}
def start(self, name: str, started_by: str = "console") -> bool:
self.calls.append(("start", name, started_by)); self.alive_set.add(name); return True
def request_stop(self, name: str, by: str = "console") -> bool:
self.calls.append(("request_stop", name)); return name in self.alive_set
def stop(self, name: str, grace_s: float = 90.0) -> bool:
self.calls.append(("stop", name)); self.alive_set.discard(name); return True
def log_tail(self, name: str, n: int = 60) -> str:
return ""
@pytest.fixture
def fake_sup(monkeypatch):
"""Point the console (fly_trader.ui.runtime.sup) at a FakeSupervisor."""
from fly_trader.ui import runtime
s = FakeSupervisor()
monkeypatch.setattr(runtime, "sup", lambda: s)
return s
@pytest.fixture
def console(fake_sup):
"""A TestClient on the console app (no workers hosted). ``console.act(name, body)`` POSTs an action with the
console's CSRF header; ``console.topic(name, **params)`` reads a topic."""
from fastapi.testclient import TestClient
from fly_trader.ui.server import create_app
import json as _json
from fly_trader.ui import runtime
hosted = runtime.HOSTS_WORKERS
app = create_app(hosts_workers=False)
with TestClient(app, base_url="http://localhost") as c:
c.act = lambda name, body=None: c.post(f"/api/action/{name}", json=body or {}, headers={"X-Fly-Console": "1"})
c.topic = lambda topic_name, **params: c.get(f"/api/topic/{topic_name}", params={"params": _json.dumps(params)} if params else None)
c.sup = fake_sup
yield c
runtime.HOSTS_WORKERS = hosted