Files
WiseGrade/backend/tests/test_import_and_model_errors.py
aerospaceboy ebe33392ca fix: improve import, evidence indexing and material-scoped grading
Preserve document revisions and evidence, harden indexing and export behavior, add explicit material selection and direct-folder import, validate model JSON schemas with safe error messages, and retain student selection while loading scoring data.
2026-10-14 13:52:56 +08:00

85 lines
3.7 KiB
Python

import json
import httpx
import pytest
from wisegrade.llm import DeepSeekClient, FactResult, ModelError
from wisegrade.storage import scan
def test_scan_selected_student_folder_prefills_cover_identity(tmp_path, monkeypatch):
from wisegrade import storage
folder = tmp_path / "test" / "示例"
folder.mkdir(parents=True)
for name in ("阶段报告1.pdf", "阶段报告2.pdf", "阶段报告3.pdf", "最终报告.pdf", "汇报PPT.pdf"):
(folder / name).write_bytes(b"fixture")
monkeypatch.setattr(storage, "first_text", lambda path: "学号:9000000001\n姓名:学生甲\n题目:机器人系统设计")
result = scan("test/示例", root=tmp_path)
assert len(result["projects"]) == 1
project = result["projects"][0]
assert project["relative_dir"] == "test/示例"
assert project["student_no"] == "9000000001"
assert project["name"] == "学生甲"
assert project["topic"] == "机器人系统设计"
assert project["status"] == "needs_confirmation"
assert len(project["files"]) == 5
assert project["missing"] == []
assert scan("test", root=tmp_path)["projects"] == result["projects"]
def test_scan_conflicting_covers_need_confirmation(tmp_path, monkeypatch):
from wisegrade import storage
folder = tmp_path / "示例"
folder.mkdir()
(folder / "阶段报告1.pdf").write_bytes(b"fixture")
(folder / "最终报告.pdf").write_bytes(b"fixture")
monkeypatch.setattr(storage, "first_text", lambda path: "学号:" +
("9000000001" if path.name.startswith("阶段") else "9000000002"))
project = scan("示例", root=tmp_path)["projects"][0]
assert project["student_no"] == ""
assert project["warnings"]
assert project["status"] == "needs_confirmation"
def test_scan_parent_skips_empty_folders(tmp_path, monkeypatch):
from wisegrade import storage
(tmp_path / "empty").mkdir()
folder = tmp_path / "9000000001_学生甲_机器人系统"
folder.mkdir()
(folder / "最终报告.pdf").write_bytes(b"fixture")
monkeypatch.setattr(storage, "first_text", lambda path: "")
projects = scan(".", root=tmp_path)["projects"]
assert len(projects) == 1
assert projects[0]["status"] == "recognized"
@pytest.mark.parametrize("code, hint", [(401, "认证"), (429, "限流"), (503, "服务")])
def test_model_http_error_is_actionable_and_redacted(code, hint):
def respond(request):
return httpx.Response(code, json={"error": "SECRET_ECHO"}, request=request)
client = DeepSeekClient(api_key="SECRET_KEY", retries=0, transport=httpx.MockTransport(respond))
with pytest.raises(ModelError) as caught:
client.json_call("测试", {}, FactResult)
assert str(code) in str(caught.value)
assert hint in str(caught.value)
assert "SECRET" not in str(caught.value)
def test_model_connect_error_explains_network_without_secret():
def respond(request):
raise httpx.ConnectError("SECRET_PROXY", request=request)
client = DeepSeekClient(api_key="SECRET_KEY", retries=0, transport=httpx.MockTransport(respond))
with pytest.raises(ModelError, match="网络.*DNS|DNS.*网络") as caught:
client.json_call("测试", {}, FactResult)
assert "SECRET" not in str(caught.value)
def test_json_call_supplies_fact_schema():
def respond(request):
body = json.loads(request.content)
assert "student_no" in body["messages"][0]["content"]
assert "goals" in body["messages"][0]["content"]
return httpx.Response(200, json={"choices": [{"message": {"content": '{"goals": ["控制系统"]}'}}]}, request=request)
client = DeepSeekClient(api_key="test", retries=0, transport=httpx.MockTransport(respond))
assert client.json_call("提取事实", {}, FactResult).goals == ["控制系统"]