Add test coverage for CLI commands, Flask routes, and shared DB methods
74 new tests across 3 files: - test_cli.py: CLI help, version, config, report generation, wg/viz/pipeline subcommands - test_web.py: All public pages, admin pages (dev/prod), API JSON endpoints, CSV export, 404s - test_db_shared.py: rated_count, gap_count, false_positive_names, category_counts, source_counts, draft_author_count_map, search_gaps, search_authors Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
269
tests/test_web.py
Normal file
269
tests/test_web.py
Normal file
@@ -0,0 +1,269 @@
|
||||
"""Tests for Flask web routes using the test client.
|
||||
|
||||
Uses the real data/drafts.db so templates can render with actual data.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
# Ensure src is on path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
|
||||
|
||||
from webui.app import create_app
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
"""Create a Flask test client in dev mode (admin enabled)."""
|
||||
# Reset auth module state so create_app can re-initialize
|
||||
import webui.auth as auth_mod
|
||||
auth_mod._initialized = False
|
||||
auth_mod._dev_mode = False
|
||||
|
||||
app = create_app(dev=True)
|
||||
app.config["TESTING"] = True
|
||||
with app.test_client() as c:
|
||||
yield c
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def prod_client():
|
||||
"""Create a Flask test client in production mode (admin disabled)."""
|
||||
import webui.auth as auth_mod
|
||||
auth_mod._initialized = False
|
||||
auth_mod._dev_mode = False
|
||||
|
||||
app = create_app(dev=False)
|
||||
app.config["TESTING"] = True
|
||||
with app.test_client() as c:
|
||||
yield c
|
||||
|
||||
|
||||
# ── Public pages ──────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def test_overview_page(client):
|
||||
resp = client.get("/")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
def test_drafts_page(client):
|
||||
resp = client.get("/drafts")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
def test_authors_page(client):
|
||||
resp = client.get("/authors")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
def test_timeline_page(client):
|
||||
resp = client.get("/timeline")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
def test_search_page_empty(client):
|
||||
resp = client.get("/search")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
def test_search_page_with_query(client):
|
||||
resp = client.get("/search?q=agent")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
def test_about_page(client):
|
||||
resp = client.get("/about")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
def test_ratings_page(client):
|
||||
resp = client.get("/ratings")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
def test_citations_page(client):
|
||||
resp = client.get("/citations")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
def test_ideas_page(client):
|
||||
resp = client.get("/ideas")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
def test_idea_clusters_page(client):
|
||||
resp = client.get("/idea-clusters")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
def test_architecture_page(client):
|
||||
resp = client.get("/architecture")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
def test_impressum_page(client):
|
||||
resp = client.get("/impressum")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
def test_datenschutz_page(client):
|
||||
resp = client.get("/datenschutz")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
# ── 404 handling ──────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def test_404_for_nonexistent_page(client):
|
||||
resp = client.get("/this-page-does-not-exist")
|
||||
assert resp.status_code == 404
|
||||
|
||||
|
||||
def test_404_for_nonexistent_draft(client):
|
||||
resp = client.get("/drafts/draft-nonexistent-foobar-xyz")
|
||||
assert resp.status_code == 404
|
||||
|
||||
|
||||
# ── Admin pages (dev mode) ───────────────────────────────────────────
|
||||
|
||||
|
||||
def test_gaps_page_dev(client):
|
||||
resp = client.get("/gaps")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
def test_monitor_page_dev(client):
|
||||
resp = client.get("/monitor")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
def test_proposals_page_dev(client):
|
||||
resp = client.get("/proposals")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
# ── Admin pages (production = 404) ───────────────────────────────────
|
||||
|
||||
|
||||
def test_gaps_page_prod_hidden(prod_client):
|
||||
resp = prod_client.get("/gaps")
|
||||
assert resp.status_code == 404
|
||||
|
||||
|
||||
def test_monitor_page_prod_hidden(prod_client):
|
||||
resp = prod_client.get("/monitor")
|
||||
assert resp.status_code == 404
|
||||
|
||||
|
||||
def test_proposals_page_prod_hidden(prod_client):
|
||||
resp = prod_client.get("/proposals")
|
||||
assert resp.status_code == 404
|
||||
|
||||
|
||||
# ── API endpoints ─────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def test_api_stats(client):
|
||||
resp = client.get("/api/stats")
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert isinstance(data, dict)
|
||||
assert "total_drafts" in data or "drafts" in data or len(data) > 0
|
||||
|
||||
|
||||
def test_api_drafts(client):
|
||||
resp = client.get("/api/drafts")
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert isinstance(data, dict)
|
||||
|
||||
|
||||
def test_api_ratings(client):
|
||||
resp = client.get("/api/ratings")
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert isinstance(data, dict)
|
||||
|
||||
|
||||
def test_api_timeline(client):
|
||||
resp = client.get("/api/timeline")
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert isinstance(data, (dict, list))
|
||||
|
||||
|
||||
def test_api_categories(client):
|
||||
resp = client.get("/api/categories")
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert isinstance(data, dict)
|
||||
|
||||
|
||||
def test_api_ideas(client):
|
||||
resp = client.get("/api/ideas")
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert isinstance(data, dict)
|
||||
|
||||
|
||||
def test_api_search_empty(client):
|
||||
resp = client.get("/api/search")
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert "drafts" in data
|
||||
|
||||
|
||||
def test_api_search_with_query(client):
|
||||
resp = client.get("/api/search?q=agent")
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert isinstance(data, dict)
|
||||
|
||||
|
||||
def test_api_draft_detail_not_found(client):
|
||||
resp = client.get("/api/drafts/nonexistent-draft-xyz")
|
||||
assert resp.status_code == 404
|
||||
data = resp.get_json()
|
||||
assert "error" in data
|
||||
|
||||
|
||||
def test_api_architecture(client):
|
||||
resp = client.get("/api/architecture")
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert isinstance(data, dict)
|
||||
|
||||
|
||||
def test_api_idea_clusters(client):
|
||||
resp = client.get("/api/idea-clusters")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
def test_api_citations(client):
|
||||
resp = client.get("/api/citations")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
def test_api_author_network(client):
|
||||
resp = client.get("/api/authors/network")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
# ── CSV export format ─────────────────────────────────────────────────
|
||||
|
||||
|
||||
def test_api_drafts_csv(client):
|
||||
resp = client.get("/api/drafts?format=csv")
|
||||
assert resp.status_code == 200
|
||||
assert "text/csv" in resp.content_type
|
||||
|
||||
|
||||
def test_api_categories_csv(client):
|
||||
resp = client.get("/api/categories?format=csv")
|
||||
assert resp.status_code == 200
|
||||
assert "text/csv" in resp.content_type
|
||||
Reference in New Issue
Block a user