"""Tests for src/webui/data.py data access functions.""" from __future__ import annotations import sys from functools import wraps from pathlib import Path import pytest # Ensure webui is importable _project_root = Path(__file__).resolve().parent.parent if str(_project_root / "src") not in sys.path: sys.path.insert(0, str(_project_root / "src")) from webui.data import ( get_overview_stats, get_category_counts, get_drafts_page, get_draft_detail, get_ideas_by_type, get_all_gaps, get_timeline_data, get_top_authors, ) def _skip_on_missing_module(fn): """Decorator that skips tests when webui.data references unavailable modules.""" @wraps(fn) def wrapper(*args, **kwargs): try: return fn(*args, **kwargs) except (ModuleNotFoundError, AttributeError) as e: pytest.skip(f"webui.data depends on module not in this worktree: {e}") return wrapper def test_get_overview_stats(seeded_db): """Overview stats should return correct counts from seeded data.""" stats = get_overview_stats(seeded_db) assert stats["total_drafts"] == 5 assert stats["rated_count"] == 5 assert stats["author_count"] == 3 # 2 + 1 + 1 = 4 ideas in seeded data assert stats["idea_count"] == 4 assert stats["gap_count"] == 0 assert "input_tokens" in stats assert "output_tokens" in stats def test_get_category_counts(seeded_db): """Category counts should reflect the seeded ratings.""" counts = get_category_counts(seeded_db) assert isinstance(counts, dict) assert "A2A protocols" in counts assert counts["A2A protocols"] == 1 assert "ML traffic mgmt" in counts @_skip_on_missing_module def test_get_drafts_page_basic(seeded_db): """Drafts page should return paginated results.""" result = get_drafts_page(seeded_db, page=1, per_page=3) assert len(result["drafts"]) == 3 assert result["total"] == 5 assert result["page"] == 1 assert result["per_page"] == 3 assert result["pages"] == 2 @_skip_on_missing_module def test_get_drafts_page_with_category_filter(seeded_db): """Filtering by category should narrow results.""" result = get_drafts_page(seeded_db, category="A2A protocols") assert result["total"] == 1 assert result["drafts"][0]["categories"] == ["A2A protocols"] @_skip_on_missing_module def test_get_drafts_page_with_search_filter(seeded_db): """Text search should filter by name/title/summary.""" result = get_drafts_page(seeded_db, search="alpha") assert result["total"] == 1 assert "alpha" in result["drafts"][0]["name"] @_skip_on_missing_module def test_get_drafts_page_empty_search(seeded_db): """Search for non-matching term should return 0 results.""" result = get_drafts_page(seeded_db, search="zzzznonexistent") assert result["total"] == 0 assert result["drafts"] == [] @_skip_on_missing_module def test_get_draft_detail(seeded_db): """Draft detail should include draft, rating, authors, ideas, refs.""" detail = get_draft_detail(seeded_db, "draft-alpha-agent-comm") assert detail is not None assert detail["name"] == "draft-alpha-agent-comm" assert detail["title"] == "Alpha Agent Communication" assert "rating" in detail assert detail["rating"]["novelty"] == 4 assert len(detail["authors"]) == 2 assert len(detail["ideas"]) == 2 assert len(detail["refs"]) == 3 @_skip_on_missing_module def test_get_draft_detail_not_found(seeded_db): """Draft detail for non-existent draft should return None.""" assert get_draft_detail(seeded_db, "draft-nonexistent") is None def test_get_ideas_by_type(seeded_db): """Ideas by type should group and count correctly.""" result = get_ideas_by_type(seeded_db) assert result["total"] == 4 assert "by_type" in result assert isinstance(result["by_type"], dict) # We have protocol, mechanism, extension types assert "protocol" in result["by_type"] or "mechanism" in result["by_type"] def test_get_all_gaps_empty(seeded_db): """With no gaps inserted, should return empty list.""" gaps = get_all_gaps(seeded_db) assert gaps == [] def test_get_all_gaps_with_data(seeded_db): """After inserting gaps, should return them.""" seeded_db.insert_gaps([ {"topic": "Gap A", "description": "Desc A", "severity": "high", "evidence": "Ev A"}, ]) gaps = get_all_gaps(seeded_db) assert len(gaps) == 1 assert gaps[0]["topic"] == "Gap A" def test_get_timeline_data(seeded_db): """Timeline data should group drafts by month.""" data = get_timeline_data(seeded_db) assert "months" in data assert "series" in data assert "categories" in data # Seeded drafts span Jan-May 2025 assert len(data["months"]) >= 1 def test_get_top_authors(seeded_db): """Top authors should return ranked list with draft counts.""" authors = get_top_authors(seeded_db, limit=10) assert len(authors) >= 1 assert "name" in authors[0] assert "draft_count" in authors[0] assert authors[0]["draft_count"] >= 2