test_dashboard_api.py 4.06 KB
"""Test /api/dashboard endpoint"""
import pytest
import sys
import os

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

from app import app as flask_app


@pytest.fixture
def client():
    flask_app.config['TESTING'] = True
    with flask_app.test_client() as c:
        yield c


def test_dashboard_returns_200(client):
    """Dashboard API returns HTTP 200"""
    r = client.get('/api/dashboard')
    assert r.status_code == 200


def test_dashboard_returns_json(client):
    """Dashboard API returns valid JSON"""
    r = client.get('/api/dashboard')
    data = r.get_json()
    assert data is not None
    assert data.get('ok') is True


def test_dashboard_has_all_modules(client):
    """Dashboard response includes all metric modules"""
    r = client.get('/api/dashboard')
    data = r.get_json()['data']
    required_modules = ['chats', 'articles', 'learn', 'reports', 'jobs',
                        'auto_reply', 'circle']
    for module in required_modules:
        assert module in data, f"Missing module: {module}"


def test_chats_module(client):
    """Chats module has expected fields"""
    r = client.get('/api/dashboard')
    chats = r.get_json()['data']['chats']
    fields = ['groups', 'messages', 'speakers', 'shares']
    for f in fields:
        assert f in chats, f"Missing chats.{f}"


def test_articles_module(client):
    """Articles module has expected fields"""
    r = client.get('/api/dashboard')
    articles = r.get_json()['data']['articles']
    fields = ['total', 'summarized', 'unsummarized', 'learned', 'unlearned']
    for f in fields:
        assert f in articles, f"Missing articles.{f}"


def test_learn_module(client):
    """Learn module has expected fields"""
    r = client.get('/api/dashboard')
    learn = r.get_json()['data']['learn']
    fields = ['pending_review', 'approved', 'applied', 'rejected', 'unclassified']
    for f in fields:
        assert f in learn, f"Missing learn.{f}"
    assert 'by_type' in learn
    for t in ['skill', 'memory', 'todo', 'reference']:
        assert t in learn['by_type'], f"Missing learn.by_type.{t}"


def test_reports_module(client):
    """Reports module has expected fields"""
    r = client.get('/api/dashboard')
    reports = r.get_json()['data']['reports']
    fields = ['daily', 'summaries', 'learn_records', 'images']
    for f in fields:
        assert f in reports, f"Missing reports.{f}"
    assert 'last_generated' in reports


def test_jobs_module(client):
    """Jobs module has expected fields"""
    r = client.get('/api/dashboard')
    jobs = r.get_json()['data']['jobs']
    fields = ['total', 'running', 'completed', 'failed', 'success_rate']
    for f in fields:
        assert f in jobs, f"Missing jobs.{f}"
    assert 'last_completed' in jobs


def test_auto_reply_module(client):
    """Auto reply module has expected fields"""
    r = client.get('/api/dashboard')
    ar = r.get_json()['data']['auto_reply']
    fields = ['whitelist', 'pending', 'sent']
    for f in fields:
        assert f in ar, f"Missing auto_reply.{f}"


def test_circle_module(client):
    """Circle module has expected fields"""
    r = client.get('/api/dashboard')
    circle = r.get_json()['data']['circle']
    fields = ['conversations', 'messages']
    for f in fields:
        assert f in circle, f"Missing circle.{f}"


def test_values_are_integers(client):
    """All count values are integers (or null for WeChat DB failures)"""
    r = client.get('/api/dashboard')
    data = r.get_json()['data']

    def check_int(val, path):
        if val is None:
            return  # WeChat DB may be unavailable
        assert isinstance(val, (int, float)), f"{path} should be int/float, got {type(val)}: {val}"

    check_int(data['articles']['total'], 'articles.total')
    check_int(data['learn']['pending_review'], 'learn.pending_review')
    check_int(data['jobs']['total'], 'jobs.total')
    check_int(data['reports']['daily'], 'reports.daily')
    check_int(data['auto_reply']['whitelist'], 'auto_reply.whitelist')
    check_int(data['circle']['conversations'], 'circle.conversations')


if __name__ == '__main__':
    pytest.main([__file__, '-v'])