重修选课

This commit is contained in:
biss committed 2026-08-15 14:57:22 +08:00
1 parent be49a2586f
commit 57a60ff8d5
10 files changed
+7275 -30

No files matched your search

@@ -278,6 +278,110 @@ public sealed class CourseSelectionsControllerTests
Assert.Single(assignedOffering.Schedules);
}
[Fact]
public async Task Student_options_identify_retake_from_unpassed_published_grades_only()
{
await using var connection = new SqliteConnection("Data Source=:memory:");
await connection.OpenAsync();
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlite(connection)
.Options;
await using var db = new AppDbContext(options);
await db.Database.EnsureCreatedAsync();
var data = await SeedFullOfferingAsync(db);
var courseId = await db.CourseSelectionOfferings
.Where(x => x.Id == data.OfferingId)
.Select(x => x.TeachingTask!.CourseId)
.SingleAsync();
var historicalTerm = new AcademicTerm
{
Code = "2025-2",
Name = "2025—2026 学年第二学期",
AcademicYear = "2025-2026",
Season = TermSeason.Spring,
StartDate = new DateOnly(2026, 2, 23),
EndDate = new DateOnly(2026, 7, 12)
};
var historicalTask = new TeachingTask
{
TaskNumber = "2025-2-CS101-01",
Name = "程序设计基础历史教学班",
AcademicTermId = historicalTerm.Id,
CourseId = courseId,
Capacity = 40,
SchedulingMode = TeachingTaskSchedulingMode.Flexible,
Status = TeachingTaskStatus.Published
};
var gradeSheet = new GradeSheet
{
TeachingTaskId = historicalTask.Id,
Status = GradeSheetStatus.Published,
PublishedAt = DateTime.UtcNow.AddDays(-1)
};
db.AddRange(historicalTerm, historicalTask, gradeSheet);
await db.SaveChangesAsync();
db.GradeRecords.AddRange(
new GradeRecord
{
GradeSheetId = gradeSheet.Id,
StudentId = data.FirstWaiterStudentId,
TotalScore = 55
},
new GradeRecord
{
GradeSheetId = gradeSheet.Id,
StudentId = data.SecondWaiterStudentId,
TotalScore = 85
});
await db.SaveChangesAsync();
db.ChangeTracker.Clear();
var failedStudentController = new CourseSelectionsController(
db,
new StudentDataScope(data.FirstWaiterUserId));
var failedResult = Assert.IsType<OkObjectResult>(await failedStudentController
.GetStudentOptions(data.RoundId, CancellationToken.None));
var failedOffering = Assert.Single(ReadProperty<IEnumerable<StudentOfferingDto>>(
failedResult.Value, "Offerings"));
Assert.True(failedOffering.IsRetake);
Assert.IsType<CreatedResult>(await failedStudentController.Enroll(
new StudentEnrollmentRequest(data.OfferingId),
CancellationToken.None));
db.ChangeTracker.Clear();
Assert.Equal(
EnrollmentType.Retake,
await db.CourseEnrollments
.Where(x =>
x.CourseSelectionOfferingId == data.OfferingId &&
x.StudentId == data.FirstWaiterStudentId)
.Select(x => x.EnrollmentType)
.SingleAsync());
var passedStudentController = new CourseSelectionsController(
db,
new StudentDataScope(data.SecondWaiterUserId));
var passedResult = Assert.IsType<OkObjectResult>(await passedStudentController
.GetStudentOptions(data.RoundId, CancellationToken.None));
var passedOffering = Assert.Single(ReadProperty<IEnumerable<StudentOfferingDto>>(
passedResult.Value, "Offerings"));
Assert.False(passedOffering.IsRetake);
var round = await db.CourseSelectionRounds.FindAsync(data.RoundId);
round!.Scope = CourseSelectionScope.RetakeOnly;
await db.SaveChangesAsync();
db.ChangeTracker.Clear();
var retakeOnlyResult = Assert.IsType<OkObjectResult>(await passedStudentController
.GetStudentOptions(data.RoundId, CancellationToken.None));
Assert.Empty(ReadProperty<IEnumerable<StudentOfferingDto>>(
retakeOnlyResult.Value, "Offerings"));
var blockedResult = Assert.IsType<ConflictObjectResult>(await passedStudentController
.Enroll(new StudentEnrollmentRequest(data.OfferingId), CancellationToken.None));
var problem = Assert.IsType<ProblemDetails>(blockedResult.Value);
Assert.Equal("该批次仅限有未通过历史成绩的学生办理重修选课。", problem.Detail);
}
private static async Task<SeededSelection> SeedFullOfferingAsync(AppDbContext db)
{
var college = new College { Code = "CS", Name = "计算机学院" };