- 删除 11 个残留文件: continuation.md, init_plan.md, reasonix.toml, djapi/continuation.md, djapi/.serena/, djapi/.claude/, djapi/.mcp.json, .claude/skills/, docs/usage.html, docs/db_schema.md, docs/report_db_design.md - 7 个 CLAUDE-*.md 移入 docs/ 并重命名去 CLAUDE- 前缀 - 新增 4 个文档: architecture.md, development.md, api.md, deployment.md - 重写 usage.md, README.md - 修复所有过时引用和交叉链接
161 lines
6.3 KiB
Python
161 lines
6.3 KiB
Python
"""
|
||
日报查询 API 测试(GET /api/news/reports/ 与 /api/news/events/)。
|
||
|
||
通过 mock 数据库查询层(api.report.query.*),只验证视图层
|
||
参数解析 / 默认窗口 / 响应结构 / 错误处理,不依赖真实 MySQL。
|
||
"""
|
||
|
||
from datetime import timedelta
|
||
from unittest.mock import patch
|
||
|
||
from django.test import TestCase
|
||
from django.utils import timezone
|
||
from rest_framework.test import APIClient
|
||
|
||
|
||
class NewsReportsAPITest(TestCase):
|
||
"""GET /api/news/reports/ 日报查询"""
|
||
|
||
def setUp(self):
|
||
self.client = APIClient()
|
||
self.url = '/api/news/reports/'
|
||
|
||
@patch('api.report.query.fetch_reports', return_value=[])
|
||
def test_default_window_last_24h(self, mock_fetch):
|
||
resp = self.client.get(self.url)
|
||
self.assertEqual(resp.status_code, 200)
|
||
self.assertEqual(resp.json(), [])
|
||
kwargs = mock_fetch.call_args.kwargs
|
||
now = timezone.now()
|
||
self.assertEqual(kwargs['start_date'], (now - timedelta(hours=24)).date())
|
||
self.assertEqual(kwargs['end_date'], now.date())
|
||
self.assertIsNone(kwargs['report_type'])
|
||
self.assertIsNone(kwargs['report_id'])
|
||
|
||
@patch('api.report.query.fetch_reports',
|
||
return_value=[{'id': 1, 'report_date': '2026-08-01', 'report_type': 'finance'}])
|
||
def test_report_type_and_date_range(self, mock_fetch):
|
||
resp = self.client.get(self.url, {
|
||
'report_type': 'finance',
|
||
'start_date': '2026-07-01',
|
||
'end_date': '2026-08-03',
|
||
})
|
||
self.assertEqual(resp.status_code, 200)
|
||
self.assertEqual(len(resp.json()), 1)
|
||
kwargs = mock_fetch.call_args.kwargs
|
||
self.assertEqual(kwargs['report_type'], 'finance')
|
||
self.assertEqual(str(kwargs['start_date']), '2026-07-01')
|
||
self.assertEqual(str(kwargs['end_date']), '2026-08-03')
|
||
|
||
@patch('api.report.query.fetch_reports')
|
||
def test_detail_by_id(self, mock_fetch):
|
||
mock_fetch.return_value = {
|
||
'id': 10, 'report_date': '2026-08-03', 'report_type': 'finance',
|
||
'events': [{'id': 1, 'title': '事件一'}],
|
||
}
|
||
resp = self.client.get(self.url, {'id': '10'})
|
||
self.assertEqual(resp.status_code, 200)
|
||
self.assertEqual(resp.json()['id'], 10)
|
||
self.assertEqual(mock_fetch.call_args.kwargs['report_id'], 10)
|
||
|
||
@patch('api.report.query.fetch_reports', return_value=None)
|
||
def test_detail_not_found(self, mock_fetch):
|
||
resp = self.client.get(self.url, {'id': '99999'})
|
||
self.assertEqual(resp.status_code, 404)
|
||
self.assertIn('error', resp.json())
|
||
|
||
def test_invalid_report_type(self):
|
||
resp = self.client.get(self.url, {'report_type': 'xxx'})
|
||
self.assertEqual(resp.status_code, 400)
|
||
|
||
def test_invalid_date_format(self):
|
||
resp = self.client.get(self.url, {'start_date': '2026/07/01'})
|
||
self.assertEqual(resp.status_code, 400)
|
||
|
||
def test_invalid_id(self):
|
||
resp = self.client.get(self.url, {'id': 'abc'})
|
||
self.assertEqual(resp.status_code, 400)
|
||
|
||
def test_non_positive_id(self):
|
||
resp = self.client.get(self.url, {'id': '0'})
|
||
self.assertEqual(resp.status_code, 400)
|
||
|
||
@patch('api.report.query.fetch_reports', side_effect=RuntimeError('db down'))
|
||
def test_query_error_500(self, mock_fetch):
|
||
resp = self.client.get(self.url)
|
||
self.assertEqual(resp.status_code, 500)
|
||
self.assertIn('error', resp.json())
|
||
|
||
|
||
class NewsEventsAPITest(TestCase):
|
||
"""GET /api/news/events/ 重要事件聚合"""
|
||
|
||
def setUp(self):
|
||
self.client = APIClient()
|
||
self.url = '/api/news/events/'
|
||
|
||
@patch('api.report.query.fetch_important_events', return_value=[])
|
||
def test_defaults(self, mock_fetch):
|
||
resp = self.client.get(self.url)
|
||
self.assertEqual(resp.status_code, 200)
|
||
self.assertEqual(resp.json(), [])
|
||
kwargs = mock_fetch.call_args.kwargs
|
||
self.assertEqual(kwargs['days'], 7)
|
||
self.assertEqual(kwargs['importance'], 4)
|
||
self.assertEqual(kwargs['limit'], 100)
|
||
self.assertIsNone(kwargs['report_type'])
|
||
self.assertIsNone(kwargs['section'])
|
||
|
||
@patch('api.report.query.fetch_important_events',
|
||
return_value=[{'id': 1, 'title': '重要事件', 'importance': 5}])
|
||
def test_filters(self, mock_fetch):
|
||
resp = self.client.get(self.url, {
|
||
'days': '3', 'importance': '5',
|
||
'report_type': 'intl', 'section': 'intl', 'limit': '10',
|
||
})
|
||
self.assertEqual(resp.status_code, 200)
|
||
self.assertEqual(len(resp.json()), 1)
|
||
kwargs = mock_fetch.call_args.kwargs
|
||
self.assertEqual(kwargs['days'], 3)
|
||
self.assertEqual(kwargs['importance'], 5)
|
||
self.assertEqual(kwargs['limit'], 10)
|
||
self.assertEqual(kwargs['report_type'], 'intl')
|
||
self.assertEqual(kwargs['section'], 'intl')
|
||
|
||
@patch('api.report.query.fetch_important_events',
|
||
return_value=[{'id': 1, 'title': 'x', 'source': 'yicai',
|
||
'sources': ['yicai', 'stcn']}])
|
||
def test_sources_field_passthrough(self, mock_fetch):
|
||
"""事件聚合响应原样透传 sources(JSON 数组)"""
|
||
resp = self.client.get(self.url)
|
||
self.assertEqual(resp.status_code, 200)
|
||
body = resp.json()
|
||
self.assertEqual(body[0]['sources'], ['yicai', 'stcn'])
|
||
self.assertEqual(body[0]['source'], 'yicai')
|
||
|
||
def test_invalid_days(self):
|
||
resp = self.client.get(self.url, {'days': 'abc'})
|
||
self.assertEqual(resp.status_code, 400)
|
||
|
||
def test_days_out_of_range(self):
|
||
resp = self.client.get(self.url, {'days': '0'})
|
||
self.assertEqual(resp.status_code, 400)
|
||
|
||
def test_invalid_importance(self):
|
||
resp = self.client.get(self.url, {'importance': '9'})
|
||
self.assertEqual(resp.status_code, 400)
|
||
|
||
def test_invalid_section(self):
|
||
resp = self.client.get(self.url, {'section': 'foo'})
|
||
self.assertEqual(resp.status_code, 400)
|
||
|
||
def test_invalid_report_type(self):
|
||
resp = self.client.get(self.url, {'report_type': 'xxx'})
|
||
self.assertEqual(resp.status_code, 400)
|
||
|
||
@patch('api.report.query.fetch_important_events', side_effect=RuntimeError('db down'))
|
||
def test_query_error_500(self, mock_fetch):
|
||
resp = self.client.get(self.url)
|
||
self.assertEqual(resp.status_code, 500)
|
||
self.assertIn('error', resp.json())
|