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>
270 lines
6.8 KiB
Python
270 lines
6.8 KiB
Python
"""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
|