Files
Timmy-time-dashboard/tests/test_smoke.py
Trip T f6a6c0f62e
All checks were successful
Tests / lint (pull_request) Successful in 2s
Tests / test (pull_request) Successful in 32s
feat: upgrade to qwen3.5, self-hosted Gitea CI, optimize Docker image
Model upgrade:
- qwen2.5:14b → qwen3.5:latest across config, tools, and docs
- Added qwen3.5 to multimodal model registry

Self-hosted Gitea CI:
- .gitea/workflows/tests.yml: lint + test jobs via act_runner
- Unified Dockerfile: pre-baked deps from poetry.lock for fast CI
- sitepackages=true in tox for ~2s dep resolution (was ~40s)
- OLLAMA_URL set to dead port in CI to prevent real LLM calls

Test isolation fixes:
- Smoke test fixture mocks create_timmy (was hitting real Ollama)
- WebSocket sends initial_state before joining broadcast pool (race fix)
- Tests use settings.ollama_model/url instead of hardcoded values
- skip_ci marker for Ollama-dependent tests, excluded in CI tox envs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 18:36:42 -04:00

244 lines
7.1 KiB
Python

"""Smoke tests — verify every major page loads without uncaught exceptions.
These tests catch regressions that unit tests miss: import errors,
template rendering failures, database schema mismatches, and startup
crashes. They run fast (no Ollama needed) and should stay green on
every commit.
"""
from unittest.mock import MagicMock, patch
import pytest
from fastapi.testclient import TestClient
@pytest.fixture
def client():
from dashboard.app import app
# Block all LLM calls so smoke tests never hit a real Ollama
mock_run = MagicMock()
mock_run.content = "Smoke test — no LLM."
mock_agent = MagicMock()
mock_agent.run.return_value = mock_run
with patch("timmy.agent.create_timmy", return_value=mock_agent):
with TestClient(app, raise_server_exceptions=False) as c:
yield c
# ---------------------------------------------------------------------------
# Core pages — these MUST return 200
# ---------------------------------------------------------------------------
class TestCorePages:
"""Every core dashboard page loads without error."""
def test_index(self, client):
r = client.get("/")
assert r.status_code == 200
def test_health(self, client):
r = client.get("/health")
assert r.status_code == 200
def test_health_status(self, client):
r = client.get("/health/status")
assert r.status_code == 200
def test_agent_panel(self, client):
r = client.get("/agents/default/panel")
assert r.status_code == 200
def test_agent_history(self, client):
r = client.get("/agents/default/history")
assert r.status_code == 200
# ---------------------------------------------------------------------------
# Feature pages — should return 200 (or 307 redirect, never 500)
# ---------------------------------------------------------------------------
class TestFeaturePages:
"""Feature pages load without 500 errors."""
def test_briefing(self, client):
r = client.get("/briefing")
assert r.status_code in (200, 307)
def test_thinking(self, client):
r = client.get("/thinking")
assert r.status_code == 200
def test_tools(self, client):
r = client.get("/tools")
assert r.status_code == 200
def test_memory(self, client):
r = client.get("/memory")
assert r.status_code == 200
def test_calm(self, client):
r = client.get("/calm")
assert r.status_code == 200
def test_tasks(self, client):
r = client.get("/tasks")
assert r.status_code == 200
def test_work_orders_queue(self, client):
r = client.get("/work-orders/queue")
assert r.status_code == 200
def test_mobile(self, client):
r = client.get("/mobile")
assert r.status_code == 200
def test_spark(self, client):
r = client.get("/spark")
assert r.status_code in (200, 307)
def test_models(self, client):
r = client.get("/models")
assert r.status_code == 200
def test_swarm_live(self, client):
r = client.get("/swarm/live")
assert r.status_code == 200
def test_swarm_events(self, client):
r = client.get("/swarm/events")
assert r.status_code == 200
def test_marketplace(self, client):
r = client.get("/marketplace")
assert r.status_code in (200, 307)
# ---------------------------------------------------------------------------
# JSON API endpoints — should return valid JSON, never 500
# ---------------------------------------------------------------------------
class TestAPIEndpoints:
"""API endpoints return valid JSON without server errors."""
def test_health_json(self, client):
r = client.get("/health")
assert r.status_code == 200
data = r.json()
assert "status" in data
def test_health_components(self, client):
r = client.get("/health/components")
assert r.status_code == 200
def test_health_sovereignty(self, client):
r = client.get("/health/sovereignty")
assert r.status_code == 200
def test_queue_status(self, client):
r = client.get("/api/queue/status")
assert r.status_code == 200
def test_tasks_api(self, client):
r = client.get("/api/tasks")
assert r.status_code == 200
def test_chat_history(self, client):
r = client.get("/api/chat/history")
assert r.status_code == 200
def test_tools_stats(self, client):
r = client.get("/tools/api/stats")
assert r.status_code == 200
def test_thinking_api(self, client):
r = client.get("/thinking/api")
assert r.status_code == 200
def test_notifications_api(self, client):
r = client.get("/api/notifications")
assert r.status_code == 200
def test_providers_api(self, client):
r = client.get("/router/api/providers")
assert r.status_code == 200
def test_mobile_status(self, client):
r = client.get("/mobile/status")
assert r.status_code == 200
def test_discord_status(self, client):
r = client.get("/discord/status")
assert r.status_code == 200
def test_telegram_status(self, client):
r = client.get("/telegram/status")
assert r.status_code == 200
def test_grok_status(self, client):
r = client.get("/grok/status")
assert r.status_code == 200
def test_paperclip_status(self, client):
r = client.get("/api/paperclip/status")
assert r.status_code == 200
# ---------------------------------------------------------------------------
# No 500s — every GET route should survive without server error
# ---------------------------------------------------------------------------
class TestNo500:
"""Verify that no page returns a 500 Internal Server Error."""
@pytest.mark.parametrize(
"path",
[
"/",
"/health",
"/health/status",
"/health/sovereignty",
"/health/components",
"/agents/default/panel",
"/agents/default/history",
"/briefing",
"/thinking",
"/thinking/api",
"/tools",
"/tools/api/stats",
"/memory",
"/calm",
"/tasks",
"/tasks/pending",
"/tasks/active",
"/tasks/completed",
"/work-orders/queue",
"/work-orders/queue/pending",
"/work-orders/queue/active",
"/mobile",
"/mobile/status",
"/spark",
"/models",
"/swarm/live",
"/swarm/events",
"/marketplace",
"/api/queue/status",
"/api/tasks",
"/api/chat/history",
"/api/notifications",
"/router/api/providers",
"/discord/status",
"/telegram/status",
"/grok/status",
"/grok/stats",
"/api/paperclip/status",
],
)
def test_no_500(self, client, path):
r = client.get(path)
assert r.status_code != 500, f"GET {path} returned 500"