from __future__ import annotations import pytest from fastapi.testclient import TestClient from chat.app import app from chat.db.connection import open_db from chat.eventlog.log import append_event from chat.eventlog.projector import project @pytest.fixture def client(tmp_path, monkeypatch): cfg = tmp_path / "config.toml" cfg.write_text('featherless_api_key = "test"\n') monkeypatch.setenv("CHAT_CONFIG_PATH", str(cfg)) db = tmp_path / "test.db" monkeypatch.setenv("CHAT_DB_PATH", str(db)) with TestClient(app) as c: if hasattr(app.state, "background_worker"): app.state.background_worker.enabled = False yield c def test_root_redirects_to_settings_when_no_you(client): response = client.get("/", follow_redirects=False) assert response.status_code == 303 assert response.headers["location"] == "/settings" def test_chats_redirects_to_settings_when_no_you(client): response = client.get("/chats", follow_redirects=False) assert response.status_code == 303 assert response.headers["location"] == "/settings" def test_redirects_to_bots_new_when_you_exists_but_no_bots(client, tmp_path): with open_db(tmp_path / "test.db") as conn: append_event( conn, kind="you_authored", payload={ "name": "Me", "pronouns": "they/them", "persona": "engineer", }, ) project(conn) response = client.get("/chats", follow_redirects=False) assert response.status_code == 303 assert response.headers["location"] == "/bots/new" def test_root_redirects_to_bots_new_when_you_exists_but_no_bots(client, tmp_path): with open_db(tmp_path / "test.db") as conn: append_event( conn, kind="you_authored", payload={ "name": "Me", "pronouns": "they/them", "persona": "engineer", }, ) project(conn) response = client.get("/", follow_redirects=False) assert response.status_code == 303 assert response.headers["location"] == "/bots/new" def test_no_redirect_when_setup_complete(client, tmp_path): with open_db(tmp_path / "test.db") as conn: append_event( conn, kind="you_authored", payload={ "name": "Me", "pronouns": "they/them", "persona": "engineer", }, ) append_event( conn, kind="bot_authored", payload={ "id": "bot_a", "name": "BotA", "persona": "...", "voice_samples": [], "traits": [], "backstory": "", "initial_relationship_to_you": "", "kickoff_prose": "", }, ) project(conn) response = client.get("/chats", follow_redirects=False) # /chats page renders normally (200) instead of redirecting. assert response.status_code == 200 def test_settings_page_accessible_without_you(client): """Don't redirect FROM /settings — user needs to fill it out.""" response = client.get("/settings", follow_redirects=False) assert response.status_code == 200 def test_bots_new_accessible_without_redirect(client, tmp_path): with open_db(tmp_path / "test.db") as conn: append_event( conn, kind="you_authored", payload={"name": "Me", "pronouns": "", "persona": ""}, ) project(conn) response = client.get("/bots/new", follow_redirects=False) assert response.status_code == 200 def test_bots_list_accessible_without_redirect_when_empty(client, tmp_path): """The bot list page itself should never redirect — even when empty.""" with open_db(tmp_path / "test.db") as conn: append_event( conn, kind="you_authored", payload={"name": "Me", "pronouns": "", "persona": ""}, ) project(conn) response = client.get("/bots", follow_redirects=False) assert response.status_code == 200 def test_post_to_settings_not_redirected(client): """POST should bypass middleware — it's a write, not a landing nav.""" response = client.post( "/settings", data={"name": "Me", "pronouns": "", "persona": ""}, follow_redirects=False, ) # Settings POST returns 200 with the saved page (no HTTPException raised). assert response.status_code == 200 def test_health_endpoint_not_redirected(client): response = client.get("/health", follow_redirects=False) assert response.status_code == 200