121 lines
4.1 KiB
Python
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
|