"""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'])