diff --git a/lyra/web/server.py b/lyra/web/server.py index 2759781..0d024b3 100644 --- a/lyra/web/server.py +++ b/lyra/web/server.py @@ -121,6 +121,49 @@ def create_app() -> FastAPI: logbus.log("info", "session edited", id=session_id, fields=list(body)) return {"ok": s is not None, "session": s} + @app.post("/session/stack") + async def session_log_stack(request: Request) -> dict: + """Log Brian's current stack directly (no LLM). Server-stamps the time.""" + body = await request.json() + try: + amount = float(body.get("amount")) + except (TypeError, ValueError): + return {"ok": False, "error": "amount must be a number"} + note = (body.get("note") or "").strip() or None + try: + state = await asyncio.to_thread(poker.log_stack, amount, note) + except ValueError as exc: + return {"ok": False, "error": str(exc)} + logbus.log("info", "stack logged (direct)", amount=amount) + return {"ok": True, "stack": state} + + @app.post("/session/buyin") + async def session_add_buyin(request: Request) -> dict: + """Add a buy-in/rebuy directly (no LLM).""" + body = await request.json() + try: + amount = float(body.get("amount")) + except (TypeError, ValueError): + return {"ok": False, "error": "amount must be a number"} + try: + total = await asyncio.to_thread(poker.add_buyin, amount) + except ValueError as exc: + return {"ok": False, "error": str(exc)} + logbus.log("info", "buyin added (direct)", amount=amount) + return {"ok": True, "buy_in_total": total} + + @app.post("/session") + async def session_start(request: Request) -> dict: + """Open a new live session directly (no LLM).""" + body = await request.json() + sid = await asyncio.to_thread(lambda: poker.start_session( + venue=body.get("venue"), stakes=body.get("stakes"), + game=body.get("game") or "NLH", fmt=body.get("format") or "cash", + buy_in=body.get("buy_in") or 0, mantra=body.get("mantra"), + )) + logbus.log("info", "poker session started (direct)", id=sid) + return {"ok": True, "id": sid} + @app.delete("/session/entry/{kind}/{entry_id}") async def delete_entry(kind: str, entry_id: int) -> dict: """Delete one HUD entry (hand | stack | read | ritual) by id.""" diff --git a/tests/test_poker_api.py b/tests/test_poker_api.py new file mode 100644 index 0000000..2fc3adc --- /dev/null +++ b/tests/test_poker_api.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +import importlib + +import pytest + + +@pytest.fixture +def client(tmp_path, monkeypatch): + monkeypatch.setenv("LYRA_DB_PATH", str(tmp_path / "test.db")) + from lyra import llm + monkeypatch.setattr(llm, "embed", lambda texts: [[0.1, 0.2, 0.3] for _ in texts]) + import lyra.memory as memory + importlib.reload(memory) + import lyra.poker as poker + importlib.reload(poker) + import lyra.web.server as server + importlib.reload(server) + from fastapi.testclient import TestClient + return TestClient(server.app), poker + + +def test_post_stack_logs_and_returns_state(client): + c, poker = client + poker.start_session(venue="Meadows", stakes="1/3", buy_in=400) + r = c.post("/session/stack", json={"amount": 373}) + assert r.status_code == 200 + body = r.json() + assert body["ok"] is True + assert body["stack"]["current"] == 373 + assert body["stack"]["net"] == pytest.approx(-27) + + +def test_post_stack_without_session_errors(client): + c, _ = client + r = c.post("/session/stack", json={"amount": 373}) + assert r.json()["ok"] is False + assert "error" in r.json() + + +def test_post_buyin_increments_total(client): + c, poker = client + poker.start_session(buy_in=400) + r = c.post("/session/buyin", json={"amount": 200}) + assert r.json()["buy_in_total"] == pytest.approx(600) + + +def test_post_session_starts_live(client): + c, poker = client + r = c.post("/session", json={"venue": "Wheeling", "stakes": "1/3", "buy_in": 400}) + sid = r.json()["id"] + assert poker.live_session()["id"] == sid