agnes-the-ai-analyst/tests/test_toolbar_integration.py
ZdenekSrotyr 85967e14ca feat(web): rename /install → /setup; nav label 'Setup local agent'
- Add GET /setup serving install.html (CLI + Claude Code setup page)
- Add GET /install → 301 redirect to /setup for backwards compat
- Move first-time setup wizard from /setup to /first-time-setup
- Update nav link: href=/setup, label 'Setup local agent', active on both /setup and /install paths
- Update page <title> to 'Setup local agent — …'
- Update /dashboard and /setup comment in _claude_setup_instructions.jinja
- Update tests and OpenAPI snapshot accordingly
2026-05-03 16:12:13 +02:00

125 lines
4.4 KiB
Python

"""Integration tests for the fastapi-debug-toolbar wiring.
Verifies that:
1. Toolbar HTML is NOT injected when DEBUG is unset (prod default).
2. The X-Request-ID header is always present (RequestIdMiddleware mounts
independently of DEBUG).
3. Toolbar markup IS injected on at least one HTML 200 response when DEBUG=1
and LOCAL_DEV_MODE=1 (auth bypass keeps a route reachable in TestClient).
"""
from __future__ import annotations
import importlib
import logging
import pytest
from fastapi.testclient import TestClient
@pytest.fixture
def reset_logging_state():
"""Reset module-level idempotency guard so each app reload re-applies setup_logging."""
import app.logging_config as lc
lc._CONFIGURED = False
yield
lc._CONFIGURED = False
logging.getLogger().handlers.clear()
@pytest.fixture
def app_with_toolbar(monkeypatch, tmp_path, reset_logging_state):
monkeypatch.setenv("DEBUG", "1")
monkeypatch.setenv("LOCAL_DEV_MODE", "1")
monkeypatch.setenv("DATA_DIR", str(tmp_path))
monkeypatch.setenv("SESSION_SECRET", "x" * 64)
import app.main as main_mod
importlib.reload(main_mod)
return main_mod.app
@pytest.fixture
def app_no_toolbar(monkeypatch, tmp_path, reset_logging_state):
monkeypatch.delenv("DEBUG", raising=False)
monkeypatch.setenv("DATA_DIR", str(tmp_path))
monkeypatch.setenv("SESSION_SECRET", "x" * 64)
import app.main as main_mod
importlib.reload(main_mod)
return main_mod.app
@pytest.mark.integration
def test_no_toolbar_when_debug_off(app_no_toolbar):
client = TestClient(app_no_toolbar)
resp = client.get("/first-time-setup", follow_redirects=False)
if resp.status_code in (302, 401):
# Auth redirect — toolbar wouldn't render anyway. The point of this
# test is to assert markup ABSENCE; no markup, no failure.
return
body = resp.text.lower()
assert "djdt" not in body, "toolbar markup should not appear when DEBUG is unset"
assert "fastdebug" not in body, "toolbar markup should not appear when DEBUG is unset"
@pytest.mark.integration
def test_request_id_header_always_present(app_no_toolbar):
client = TestClient(app_no_toolbar)
resp = client.get("/api/health")
assert "x-request-id" in resp.headers
@pytest.mark.integration
def test_toolbar_html_present_when_debug(app_with_toolbar):
client = TestClient(app_with_toolbar)
# Try several HTML routes — at least one should respond 200 under
# LOCAL_DEV_MODE=1 (auth bypass).
for path in ("/dashboard", "/first-time-setup", "/login", "/admin/access"):
resp = client.get(path, follow_redirects=False)
if resp.status_code == 200 and "text/html" in resp.headers.get("content-type", ""):
body = resp.text.lower()
assert "djdt" in body or "fastdebug" in body, (
f"toolbar markup missing on {path}; got first 500 chars: {body[:500]}"
)
return
pytest.skip(
"no HTML route returned 200 in TestClient; toolbar injection cannot be verified here",
)
@pytest.mark.integration
def test_db_endpoint_triggers_record_query(app_with_toolbar, monkeypatch):
"""End-to-end wiring: a request that hits DuckDB drives record_query under DEBUG=1.
The unit tests in test_duckdb_panel.py exercise InstrumentedConnection +
record_query directly. This test closes the loop: an actual FastAPI
request through the wired-up app must trigger record_query so we know
src/db.py is handing out instrumented connections under DEBUG=1.
"""
from app.debug import duckdb_panel
counter = {"calls": 0}
original = duckdb_panel.record_query
def counting_record(*args, **kwargs):
counter["calls"] += 1
return original(*args, **kwargs)
monkeypatch.setattr(duckdb_panel, "record_query", counting_record)
client = TestClient(app_with_toolbar)
# /api/health is unauthenticated and calls get_system_db().execute(...)
# via _check_db_schema(). It's the simplest DB-touching path reachable
# from TestClient without auth fixtures.
resp = client.get("/api/health")
if resp.status_code != 200 or counter["calls"] == 0:
pytest.skip(
"DB-touching endpoint not reachable or did not record queries; "
"DuckDB instrumentation contract is covered by Task 7 unit tests."
)
assert counter["calls"] > 0, (
"record_query was not invoked; src/db.py is not handing out an InstrumentedConnection under DEBUG=1"
)