Files
chat/tests/test_chat_list.py
2026-04-26 14:33:28 -04:00

121 lines
4.1 KiB
Python

from __future__ import annotations
from pathlib import Path
import pytest
from fastapi.testclient import TestClient
from chat.app import app
from chat.eventlog.log import append_event
from chat.eventlog.projector import project
@pytest.fixture
def client(tmp_path, monkeypatch):
config_path = tmp_path / "config.toml"
config_path.write_text('featherless_api_key = "test"\n')
monkeypatch.setenv("CHAT_CONFIG_PATH", str(config_path))
monkeypatch.setenv("CHAT_DB_PATH", str(tmp_path / "test.db"))
with TestClient(app) as c:
yield c
def _author_you(db_path: Path) -> None:
"""Author a ``you_entity`` so the first-run middleware doesn't redirect."""
from chat.db.connection import open_db
with open_db(db_path) as conn:
append_event(
conn,
kind="you_authored",
payload={"name": "Me", "pronouns": "", "persona": ""},
)
project(conn)
def _author_bot_and_chat(db_path: Path, bot_id: str = "bot_a") -> None:
"""Insert a you_entity, bot, and chat via the event log (skip kickoff route)."""
from chat.db.connection import open_db
with open_db(db_path) as conn:
append_event(
conn,
kind="you_authored",
payload={"name": "Me", "pronouns": "", "persona": ""},
)
append_event(
conn,
kind="bot_authored",
payload={
"id": bot_id,
"name": "BotA",
"persona": "thoughtful, observant",
"voice_samples": [],
"traits": ["shy"],
"backstory": "",
"initial_relationship_to_you": "coworker",
"kickoff_prose": "you stay late at the office; she's there too",
},
)
append_event(
conn,
kind="chat_created",
payload={
"id": f"chat_{bot_id}",
"host_bot_id": bot_id,
"initial_time": "2026-04-26T20:00:00+00:00",
"narrative_anchor": "Day 1",
"weather": "",
},
)
project(conn)
def test_root_redirects_to_chats_when_setup_complete(client, tmp_path):
# With both you_entity and a bot present, the first-run middleware
# passes through and the nav router sends "/" → "/chats".
_author_bot_and_chat(tmp_path / "test.db", "bot_a")
response = client.get("/", follow_redirects=False)
assert response.status_code == 303
assert response.headers["location"] == "/chats"
def test_chats_list_empty_state(client, tmp_path):
# Author you + a bot but NO chats — should render the empty-state
# chats list, not redirect.
_author_bot_and_chat(tmp_path / "test.db", "bot_a")
# Drop the chat row so we hit the empty-state branch (the helper
# creates a chat — undo it via a fresh seed without chat_created).
from chat.db.connection import open_db
with open_db(tmp_path / "test.db") as conn:
conn.execute("DELETE FROM chats")
conn.commit()
response = client.get("/chats")
assert response.status_code == 200
body = response.text.lower()
# Empty state should mention there are no chats yet.
assert "no chats yet" in body
def test_chats_list_renders_existing_chats(client, tmp_path):
_author_bot_and_chat(tmp_path / "test.db", "bot_a")
response = client.get("/chats")
assert response.status_code == 200
body = response.text
# The bot's display name should appear in the chat row.
assert "BotA" in body
# The chat's in-fiction time should appear in the meta.
assert "2026-04-26T20:00:00+00:00" in body
def test_existing_template_routes_still_work_with_new_layout(client):
# Smoke test the layout reshuffle didn't break the existing pages.
for path in ("/bots", "/bots/new", "/settings"):
response = client.get(path)
assert response.status_code == 200, f"{path} returned {response.status_code}"
body = response.text
# Each page should now show the persistent left-rail brand link.
assert 'class="rail"' in body or "rail-brand" in body