forked from Rockachopa/Timmy-time-dashboard
feat: persistent chat history with clear button
- Add dashboard/store.py: MessageLog dataclass singleton tracking user/agent/error messages for the lifetime of the server process - agents.py: write each chat turn to MessageLog; add GET and DELETE /agents/timmy/history routes returning the history.html partial - partials/history.html: render stored messages by role (YOU / TIMMY / SYSTEM); falls back to the Mission Control init message when empty - index.html: chat-log loads history via hx-get on page start; new CLEAR button in panel header sends hx-delete to reset the log - style.css: add .mc-btn-clear (muted, red-on-hover for the header) - tests: autouse reset_message_log fixture in conftest; 5 new history tests covering empty state, recording, offline errors, clear, and post-clear state → 32 tests total, all passing https://claude.ai/code/session_01KZMfwBpLuiv6x9GbzTqbys
This commit is contained in:
@@ -18,6 +18,15 @@ for _mod in [
|
||||
sys.modules.setdefault(_mod, MagicMock())
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def reset_message_log():
|
||||
"""Clear the in-memory chat log before and after every test."""
|
||||
from dashboard.store import message_log
|
||||
message_log.clear()
|
||||
yield
|
||||
message_log.clear()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
from dashboard.app import app
|
||||
|
||||
@@ -108,3 +108,57 @@ def test_chat_timmy_ollama_offline(client):
|
||||
def test_chat_timmy_requires_message(client):
|
||||
response = client.post("/agents/timmy/chat", data={})
|
||||
assert response.status_code == 422
|
||||
|
||||
|
||||
# ── History ────────────────────────────────────────────────────────────────────
|
||||
|
||||
def test_history_empty_shows_init_message(client):
|
||||
response = client.get("/agents/timmy/history")
|
||||
assert response.status_code == 200
|
||||
assert "Mission Control initialized" in response.text
|
||||
|
||||
|
||||
def test_history_records_user_and_agent_messages(client):
|
||||
mock_agent = MagicMock()
|
||||
mock_agent.run.return_value = MagicMock(content="I am operational.")
|
||||
|
||||
with patch("dashboard.routes.agents.create_timmy", return_value=mock_agent):
|
||||
client.post("/agents/timmy/chat", data={"message": "status check"})
|
||||
|
||||
response = client.get("/agents/timmy/history")
|
||||
assert "status check" in response.text
|
||||
assert "I am operational." in response.text
|
||||
|
||||
|
||||
def test_history_records_error_when_offline(client):
|
||||
with patch("dashboard.routes.agents.create_timmy", side_effect=Exception("refused")):
|
||||
client.post("/agents/timmy/chat", data={"message": "ping"})
|
||||
|
||||
response = client.get("/agents/timmy/history")
|
||||
assert "ping" in response.text
|
||||
assert "Timmy is offline" in response.text
|
||||
|
||||
|
||||
def test_history_clear_resets_to_init_message(client):
|
||||
mock_agent = MagicMock()
|
||||
mock_agent.run.return_value = MagicMock(content="Acknowledged.")
|
||||
|
||||
with patch("dashboard.routes.agents.create_timmy", return_value=mock_agent):
|
||||
client.post("/agents/timmy/chat", data={"message": "hello"})
|
||||
|
||||
response = client.delete("/agents/timmy/history")
|
||||
assert response.status_code == 200
|
||||
assert "Mission Control initialized" in response.text
|
||||
|
||||
|
||||
def test_history_empty_after_clear(client):
|
||||
mock_agent = MagicMock()
|
||||
mock_agent.run.return_value = MagicMock(content="OK.")
|
||||
|
||||
with patch("dashboard.routes.agents.create_timmy", return_value=mock_agent):
|
||||
client.post("/agents/timmy/chat", data={"message": "test"})
|
||||
|
||||
client.delete("/agents/timmy/history")
|
||||
response = client.get("/agents/timmy/history")
|
||||
assert "test" not in response.text
|
||||
assert "Mission Control initialized" in response.text
|
||||
|
||||
Reference in New Issue
Block a user