using Jiaowu.Api.Controllers; using Jiaowu.Api.Domain.Academic; using Jiaowu.Api.Domain.Identity; using Jiaowu.Api.Infrastructure.Auth; using Jiaowu.Api.Infrastructure.Caching; using Jiaowu.Api.Infrastructure.Exams; using Jiaowu.Api.Infrastructure.Persistence; using Microsoft.AspNetCore.Mvc; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; namespace Jiaowu.Api.Tests; public sealed class ExamPlanPaginationTests { [Fact] public async Task GetPlan_PaginatesSearchesAndFiltersSessionsOnServer() { await using var connection = new SqliteConnection("Data Source=:memory:"); await connection.OpenAsync(); await using var db = new AppDbContext( new DbContextOptionsBuilder() .UseSqlite(connection) .Options); await db.Database.EnsureCreatedAsync(); var college = new College { Code = "CS", Name = "计算机学院" }; var course = new Course { Code = "CS101", Name = "程序设计基础", CollegeId = college.Id, Credits = 4, TotalHours = 64, LectureHours = 48, PracticeHours = 16, Nature = CourseNature.MajorRequired, AssessmentMethod = AssessmentMethod.Examination }; var term = new AcademicTerm { Code = "2026-1", Name = "2026—2027 学年第一学期", AcademicYear = "2026-2027", Season = TermSeason.Autumn, StartDate = new DateOnly(2026, 9, 1), EndDate = new DateOnly(2027, 1, 20) }; var tasks = Enumerable.Range(1, 11) .Select(number => new TeachingTask { TaskNumber = $"2026-1-CS101-{number:00}", Name = number == 11 ? "专项课程教学班" : $"程序设计教学班 {number}", AcademicTermId = term.Id, CourseId = course.Id, Capacity = 60, Status = TeachingTaskStatus.Published }) .ToList(); var plan = new ExamPlan { AcademicTermId = term.Id, Name = "期末考试", Status = ExamPlanStatus.Draft, Sessions = tasks.Select((task, index) => new ExamSession { TeachingTaskId = task.Id, ExamDate = new DateOnly(2027, 1, 8).AddDays(index), StartPeriod = 1, PeriodCount = 2, StartsAt = new DateTime(2027, 1, 8, 8, 0, 0, DateTimeKind.Utc) .AddDays(index), EndsAt = new DateTime(2027, 1, 8, 9, 50, 0, DateTimeKind.Utc) .AddDays(index), RequiredInvigilatorCount = 1 }).ToList() }; db.AddRange(college, course, term); db.AddRange(tasks); db.Add(plan); await db.SaveChangesAsync(); var controller = new ExamsController( db, new AllScope(), new ExamArrangementService(db), NoOpAppCache.Instance); var secondPage = ReadPlan(await controller.GetPlan( plan.Id, page: 2, pageSize: 10, cancellationToken: CancellationToken.None)); Assert.Equal(11, ReadInt(secondPage, "TotalSessionCount")); Assert.Equal(11, ReadInt(secondPage, "FilteredSessionCount")); Assert.Equal(2, ReadInt(secondPage, "SessionPage")); Assert.Single(ReadItems(secondPage, "Sessions")); Assert.Equal(11, ReadItems(secondPage, "ScheduledTeachingTaskIds").Count); var searchPage = ReadPlan(await controller.GetPlan( plan.Id, keyword: "专项", cancellationToken: CancellationToken.None)); Assert.Equal(11, ReadInt(searchPage, "TotalSessionCount")); Assert.Equal(1, ReadInt(searchPage, "FilteredSessionCount")); Assert.Single(ReadItems(searchPage, "Sessions")); var completedPage = ReadPlan(await controller.GetPlan( plan.Id, allocation: "complete", cancellationToken: CancellationToken.None)); Assert.Equal(0, ReadInt(completedPage, "FilteredSessionCount")); Assert.Empty(ReadItems(completedPage, "Sessions")); } private static object ReadPlan(ActionResult result) => Assert.IsType(result).Value!; private static int ReadInt(object value, string property) => (int)value.GetType().GetProperty(property)!.GetValue(value)!; private static List ReadItems(object value, string property) => Assert.IsAssignableFrom( value.GetType().GetProperty(property)!.GetValue(value)) .Cast() .ToList(); private sealed class AllScope : ICurrentUserDataScope { public CurrentUserScope Current { get; } = new( Guid.NewGuid(), "测试管理员", null, DataScope.All, new HashSet([SystemRoles.SuperAdmin])); } }