Files
Timmy-time-dashboard/tests/dashboard/test_dashboard.py
Alexander Whitestone 18ed6232f9 feat: Timmy fixes and improvements (#72)
* test: remove hardcoded sleeps, add pytest-timeout

- Replace fixed time.sleep() calls with intelligent polling or WebDriverWait
- Add pytest-timeout dependency and --timeout=30 to prevent hangs
- Fixes test flakiness and improves test suite speed

* feat: add Aider AI tool to Forge's toolkit

- Add Aider tool that calls local Ollama (qwen2.5:14b) for AI coding assist
- Register tool in Forge's code toolkit
- Add functional tests for the Aider tool

* config: add opencode.json with local Ollama provider for sovereign AI

* feat: Timmy fixes and improvements

## Bug Fixes
- Fix read_file path resolution: add ~ expansion, proper relative path handling
- Add repo_root to config.py with auto-detection from .git location
- Fix hardcoded llama3.2 - now dynamic from settings.ollama_model

## Timmy's Requests
- Add communication protocol to AGENTS.md (read context first, explain changes)
- Create DECISIONS.md for architectural decision documentation
- Add reasoning guidance to system prompts (step-by-step, state uncertainty)
- Update tests to reflect correct model name (llama3.1:8b-instruct)

## Testing
- All 177 dashboard tests pass
- All 32 prompt/tool tests pass

---------

Co-authored-by: Alexander Payne <apayne@MM.local>
2026-02-26 23:39:13 -05:00

176 lines
6.1 KiB
Python

from unittest.mock import AsyncMock, patch
# ── Index ─────────────────────────────────────────────────────────────────────
def test_index_returns_200(client):
response = client.get("/")
assert response.status_code == 200
def test_index_contains_title(client):
response = client.get("/")
assert "TIMMY TIME" in response.text
def test_index_contains_chat_interface(client):
response = client.get("/")
# Timmy panel loads dynamically via HTMX; verify the trigger attribute is present
assert 'hx-get="/agents/timmy/panel"' in response.text
# ── Health ────────────────────────────────────────────────────────────────────
def test_health_endpoint_ok(client):
with patch(
"dashboard.routes.health.check_ollama",
new_callable=AsyncMock,
return_value=True,
):
response = client.get("/health")
assert response.status_code == 200
data = response.json()
assert data["status"] == "ok"
assert data["services"]["ollama"] == "up"
assert "timmy" in data["agents"]
def test_health_endpoint_ollama_down(client):
with patch(
"dashboard.routes.health.check_ollama",
new_callable=AsyncMock,
return_value=False,
):
response = client.get("/health")
assert response.status_code == 200
assert response.json()["services"]["ollama"] == "down"
def test_health_status_panel_ollama_up(client):
with patch(
"dashboard.routes.health.check_ollama",
new_callable=AsyncMock,
return_value=True,
):
response = client.get("/health/status")
assert response.status_code == 200
assert "UP" in response.text
def test_health_status_panel_ollama_down(client):
with patch(
"dashboard.routes.health.check_ollama",
new_callable=AsyncMock,
return_value=False,
):
response = client.get("/health/status")
assert response.status_code == 200
assert "DOWN" in response.text
# ── Agents ────────────────────────────────────────────────────────────────────
def test_agents_list(client):
response = client.get("/agents")
assert response.status_code == 200
data = response.json()
assert "agents" in data
ids = [a["id"] for a in data["agents"]]
assert "timmy" in ids
def test_agents_list_timmy_metadata(client):
response = client.get("/agents")
timmy = next(a for a in response.json()["agents"] if a["id"] == "timmy")
assert timmy["name"] == "Timmy"
assert timmy["model"] == "llama3.1:8b-instruct"
assert timmy["type"] == "sovereign"
# ── Chat ──────────────────────────────────────────────────────────────────────
def test_chat_timmy_success(client):
with patch(
"dashboard.routes.agents.timmy_chat",
return_value="I am Timmy, operational and sovereign.",
):
response = client.post("/agents/timmy/chat", data={"message": "status?"})
assert response.status_code == 200
assert "status?" in response.text
assert "I am Timmy" in response.text
def test_chat_timmy_shows_user_message(client):
with patch("dashboard.routes.agents.timmy_chat", return_value="Acknowledged."):
response = client.post("/agents/timmy/chat", data={"message": "hello there"})
assert "hello there" in response.text
def test_chat_timmy_ollama_offline(client):
with patch(
"dashboard.routes.agents.timmy_chat",
side_effect=Exception("connection refused"),
):
response = client.post("/agents/timmy/chat", data={"message": "ping"})
assert response.status_code == 200
assert "Timmy is offline" in response.text
assert "ping" in response.text
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):
with patch("dashboard.routes.agents.timmy_chat", return_value="I am operational."):
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.timmy_chat", 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):
with patch("dashboard.routes.agents.timmy_chat", return_value="Acknowledged."):
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):
with patch("dashboard.routes.agents.timmy_chat", return_value="OK."):
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