ebe33392ca
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.
73 lines
3.4 KiB
Python
73 lines
3.4 KiB
Python
"""Explicit batch membership regressions, isolated from real grading and credentials."""
|
|
import pytest
|
|
from fastapi import HTTPException
|
|
from pydantic import ValidationError
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.orm import Session
|
|
|
|
from wisegrade import api, services
|
|
from wisegrade.db import Base
|
|
from wisegrade.models import CourseVersion, Project, ScoringBatch, Student
|
|
from wisegrade.seed import seed_initial
|
|
|
|
|
|
@pytest.fixture
|
|
def batch_db(environment):
|
|
engine = create_engine("sqlite://")
|
|
Base.metadata.create_all(engine)
|
|
with Session(engine, expire_on_commit=False) as db:
|
|
seed_initial(db)
|
|
students = [Student(student_no=f"900000000{i}", name=f"测试{i}") for i in (1, 2, 3)]
|
|
db.add_all(students)
|
|
db.flush()
|
|
other = CourseVersion(course_id=1, version=2, title="另一课程版本", published=True,
|
|
objectives=[], indicators=[], directions=[], prompts={}, templates=[])
|
|
db.add(other)
|
|
db.flush()
|
|
db.add_all([Project(student_id=students[0].id, course_version_id=1, topic="项目甲", source_dir="a"),
|
|
Project(student_id=students[1].id, course_version_id=1, topic="项目乙", source_dir="b"),
|
|
Project(student_id=students[2].id, course_version_id=other.id, topic="其他版本", source_dir="c")])
|
|
db.commit()
|
|
yield db
|
|
engine.dispose()
|
|
|
|
|
|
def test_select_one_project_and_preserve_batch_membership(batch_db):
|
|
data = api.BatchInput(course_version_id=1, rubric_version_id=1, project_ids=[2])
|
|
created = api.create_batch(data, batch_db)
|
|
batch = batch_db.get(ScoringBatch, created["id"])
|
|
assert batch.project_ids == [2]
|
|
# Importing another project later cannot change this batch's selected membership.
|
|
batch_db.add(Project(student_id=1, course_version_id=1, topic="后来导入", source_dir="later"))
|
|
batch_db.commit()
|
|
assert api.batch_progress(batch.id, batch_db)["project_ids"] == [2]
|
|
|
|
|
|
def test_omitted_selection_preserves_existing_all_projects_api(batch_db):
|
|
created = api.create_batch(api.BatchInput(course_version_id=1, rubric_version_id=1), batch_db)
|
|
assert batch_db.get(ScoringBatch, created["id"]).project_ids == [1, 2]
|
|
|
|
|
|
@pytest.mark.parametrize("ids", [[999], [3], [2, 2]])
|
|
def test_reject_unknown_cross_version_and_duplicate_project_ids(batch_db, ids):
|
|
with pytest.raises(HTTPException) as exc:
|
|
api.create_batch(api.BatchInput(course_version_id=1, rubric_version_id=1, project_ids=ids), batch_db)
|
|
assert exc.value.status_code == 400
|
|
assert batch_db.query(ScoringBatch).count() == 0
|
|
|
|
|
|
def test_empty_explicit_selection_cannot_silently_grade_everyone():
|
|
with pytest.raises(ValidationError):
|
|
api.BatchInput(course_version_id=1, rubric_version_id=1, project_ids=[])
|
|
|
|
|
|
def test_batch_list_uses_saved_human_readable_version_metadata(batch_db):
|
|
created = api.create_batch(api.BatchInput(course_version_id=1, rubric_version_id=1, project_ids=[1]), batch_db)
|
|
batch = batch_db.get(ScoringBatch, created["id"])
|
|
batch.course_snapshot = {**batch.course_snapshot, "version": 7, "title": "创建时课程"}
|
|
batch.rubric_snapshot = {**batch.rubric_snapshot, "version": 9}
|
|
batch_db.commit()
|
|
listed = api.list_batches(batch_db)[0]
|
|
assert listed["course_version"] == 7 and listed["course_title"] == "创建时课程"
|
|
assert listed["rubric_version"] == 9 and listed["criterion_count"] == 22
|