完善评估能力:筛选参数 + degraded 排除 + 前端评估页
后端: - settle_prediction 拒绝 degraded/failed(明确错误信息) - get_eval_summary 支持 provider/model/prompt_version/mode 筛选 - 返回 filtered_settled/evaluated/skipped_degraded 等计数 - matches 游标分页方向修复(scheduled ASC 用 > 条件) - available_at 加 2h 缓冲(近似完赛时间) - bzzziro 统计字段映射注释(待真实响应验证) - injuries 区分 no_local_data 与 success 空名单 前端: - 新增 EvalPage(筛选控件 + 汇总卡片 + 准确率表格) - 挂载 /admin/eval 路由与导航 测试: - test_matches_cursor.py:游标方向 - test_available_at.py:2h 缓冲与回测防泄漏 - test_bzzoirot_stats.py:统计字段映射 - test_injuries_no_local_data.py:no_local_data vs success - test_injuries_inserted_count.py:失败批不计入 - test_eval_excludes_degraded.py:degraded 排除准确率
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
"""回归测试: 比赛列表游标分页方向修复。
|
||||
|
||||
验证:
|
||||
- status=scheduled 时,游标条件为「大于」(ASC 方向)
|
||||
- 其它 status 时,游标条件为「小于」(DESC 方向)
|
||||
- 无 cursor 时行为不变
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
|
||||
import pytest
|
||||
from sqlalchemy import select
|
||||
|
||||
from src.db.models import Match
|
||||
|
||||
|
||||
class TestCursorPaginationDirection:
|
||||
"""验证游标条件方向与排序方向一致。"""
|
||||
|
||||
def _build_query(self, status=None, cursor=None):
|
||||
"""复现 list_matches 的查询构造逻辑,返回 where 条件列表。"""
|
||||
q = select(Match)
|
||||
|
||||
if cursor:
|
||||
last_date_str, last_id_str = cursor.split("|", 1)
|
||||
last_date = datetime.fromisoformat(last_date_str)
|
||||
last_id = int(last_id_str)
|
||||
if status == "scheduled":
|
||||
q = q.where(
|
||||
(Match.match_date > last_date) |
|
||||
((Match.match_date == last_date) & (Match.id > last_id))
|
||||
)
|
||||
else:
|
||||
q = q.where(
|
||||
(Match.match_date < last_date) |
|
||||
((Match.match_date == last_date) & (Match.id < last_id))
|
||||
)
|
||||
|
||||
if status:
|
||||
q = q.where(Match.match_status == status)
|
||||
|
||||
if status == "scheduled":
|
||||
order = (Match.match_date.asc(), Match.id.asc())
|
||||
else:
|
||||
order = (Match.match_date.desc(), Match.id.desc())
|
||||
|
||||
return q.order_by(*order)
|
||||
|
||||
def test_scheduled_uses_greater_than(self):
|
||||
"""scheduled + cursor: 应使用 > 条件(ASC 方向)。"""
|
||||
q = self._build_query(
|
||||
status="scheduled",
|
||||
cursor="2026-01-15T15:00:00|100"
|
||||
)
|
||||
sql = str(q)
|
||||
assert ">" in sql, f"scheduled 游标应使用 >,SQL: {sql}"
|
||||
assert "<" not in sql or "match_date <" not in sql, f"不应出现 < 条件"
|
||||
|
||||
def test_other_status_uses_less_than(self):
|
||||
"""finished + cursor: 应使用 < 条件(DESC 方向)。"""
|
||||
q = self._build_query(
|
||||
status="finished",
|
||||
cursor="2026-01-15T15:00:00|100"
|
||||
)
|
||||
sql = str(q)
|
||||
assert "<" in sql, f"finished 游标应使用 <,SQL: {sql}"
|
||||
assert "match_date >" not in sql, f"不应出现 > 条件"
|
||||
|
||||
def test_no_cursor_no_direction(self):
|
||||
"""无 cursor 时不应有游标条件。"""
|
||||
q = self._build_query(status="scheduled", cursor=None)
|
||||
sql = str(q)
|
||||
# 应无 match_date 比较条件(只有 status filter)
|
||||
assert "match_date >" not in sql
|
||||
assert "match_date <" not in sql
|
||||
|
||||
def test_scheduled_order_is_asc(self):
|
||||
"""scheduled 排序应为 ASC。"""
|
||||
q = self._build_query(status="scheduled", cursor=None)
|
||||
sql = str(q)
|
||||
assert "ASC" in sql, f"scheduled 应 ASC 排序,SQL: {sql}"
|
||||
assert "DESC" not in sql, f"不应出现 DESC"
|
||||
|
||||
def test_finished_order_is_desc(self):
|
||||
"""finished 排序应为 DESC。"""
|
||||
q = self._build_query(status="finished", cursor=None)
|
||||
sql = str(q)
|
||||
assert "DESC" in sql, f"finished 应 DESC 排序,SQL: {sql}"
|
||||
Reference in New Issue
Block a user