using Jiaowu.Api.Controllers; using Jiaowu.Api.Domain.Academic; using Jiaowu.Api.Domain.Identity; using Jiaowu.Api.Infrastructure.Auth; using Jiaowu.Api.Infrastructure.Persistence; using Microsoft.AspNetCore.Mvc; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; namespace Jiaowu.Api.Tests; public sealed class DashboardControllerTests { [Fact] public async Task College_dashboard_is_scoped_and_only_counts_actionable_stage() { await using var database = await DashboardDatabase.CreateAsync(); var controller = new DashboardController( database.Db, new TestDataScope( database.FirstCollegeId, DataScope.College, SystemRoles.CollegeAdmin)); var result = await controller.Get(CancellationToken.None); var response = Assert.IsType( Assert.IsType(result.Result).Value); Assert.Equal("College", response.Audience.Level); Assert.Equal("第一学院", response.Audience.ScopeName); Assert.Equal(1, response.Counts.Students); Assert.Equal(1, response.Counts.Teachers); Assert.Equal(1, response.Counts.TeachingTasks); Assert.Equal(1, response.Pending.TeacherApplications); Assert.Equal(1, response.Pending.StudentStatusChanges); } [Fact] public async Task School_dashboard_uses_school_stage_and_all_colleges() { await using var database = await DashboardDatabase.CreateAsync(); var controller = new DashboardController( database.Db, new TestDataScope( null, DataScope.All, SystemRoles.AcademicAdmin)); var result = await controller.Get(CancellationToken.None); var response = Assert.IsType( Assert.IsType(result.Result).Value); Assert.Equal("School", response.Audience.Level); Assert.Equal(2, response.Counts.Students); Assert.Equal(2, response.Counts.Teachers); Assert.Equal(2, response.Counts.TeachingTasks); Assert.Equal(2, response.Pending.TeacherApplications); Assert.Equal(1, response.Pending.StudentStatusChanges); } private sealed class DashboardDatabase : IAsyncDisposable { private readonly SqliteConnection connection; private DashboardDatabase( SqliteConnection connection, AppDbContext db, Guid firstCollegeId) { this.connection = connection; Db = db; FirstCollegeId = firstCollegeId; } public AppDbContext Db { get; } public Guid FirstCollegeId { get; } public static async Task CreateAsync() { var connection = new SqliteConnection("Data Source=:memory:"); await connection.OpenAsync(); var options = new DbContextOptionsBuilder() .UseSqlite(connection) .Options; var db = new AppDbContext(options); await db.Database.EnsureCreatedAsync(); 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, 15), IsCurrent = true }; var firstCollege = new College { Code = "C01", Name = "第一学院" }; var secondCollege = new College { Code = "C02", Name = "第二学院" }; var firstMajor = CreateMajor("M01", firstCollege); var secondMajor = CreateMajor("M02", secondCollege); var firstClass = CreateClass("CL01", firstMajor); var secondClass = CreateClass("CL02", secondMajor); var firstStudent = CreateStudent("S01", firstClass); var secondStudent = CreateStudent("S02", secondClass); var firstTeacher = CreateTeacher("T01", firstCollege); var secondTeacher = CreateTeacher("T02", secondCollege); var firstCourse = CreateCourse("COURSE01", firstCollege); var secondCourse = CreateCourse("COURSE02", secondCollege); var firstTask = CreateTask("TASK01", term, firstCourse); var secondTask = CreateTask("TASK02", term, secondCourse); db.AddRange( term, firstCollege, secondCollege, firstMajor, secondMajor, firstClass, secondClass, firstStudent, secondStudent, firstTeacher, secondTeacher, firstCourse, secondCourse, firstTask, secondTask, new TeacherCourseApplication { AcademicTerm = term, Teacher = firstTeacher, Course = firstCourse, Status = TeacherCourseApplicationStatus.Pending }, new TeacherCourseApplication { AcademicTerm = term, Teacher = secondTeacher, Course = secondCourse, Status = TeacherCourseApplicationStatus.Pending }, CreateStatusChange( firstStudent, StudentStatusChangeState.CounselorApproved), CreateStatusChange( firstStudent, StudentStatusChangeState.CollegeApproved), CreateStatusChange( secondStudent, StudentStatusChangeState.Submitted)); await db.SaveChangesAsync(); return new DashboardDatabase(connection, db, firstCollege.Id); } public async ValueTask DisposeAsync() { await Db.DisposeAsync(); await connection.DisposeAsync(); } private static Major CreateMajor(string code, College college) => new() { Code = code, Name = $"专业 {code}", College = college, DegreeType = "本科" }; private static AdministrativeClass CreateClass( string code, Major major) => new() { Code = code, Name = $"班级 {code}", Major = major, Grade = 2026 }; private static Student CreateStudent( string number, AdministrativeClass administrativeClass) => new() { StudentNumber = number, Name = $"学生 {number}", AdministrativeClass = administrativeClass, EnrollmentYear = 2026, EnrollmentDate = new DateOnly(2026, 9, 1), Status = StudentStatus.Active }; private static Teacher CreateTeacher( string number, College college) => new() { TeacherNumber = number, Name = $"教师 {number}", College = college, Status = TeacherStatus.Active }; private static Course CreateCourse(string code, College college) => new() { Code = code, Name = $"课程 {code}", College = college, Nature = CourseNature.MajorRequired, Credits = 2, TotalHours = 32, LectureHours = 32, AssessmentMethod = AssessmentMethod.Examination }; private static TeachingTask CreateTask( string number, AcademicTerm term, Course course) => new() { TaskNumber = number, Name = $"教学班 {number}", AcademicTerm = term, Course = course, Capacity = 40, Status = TeachingTaskStatus.Published }; private static StudentStatusChange CreateStatusChange( Student student, StudentStatusChangeState state) => new() { Student = student, Type = StudentStatusChangeType.Suspension, OriginalStatus = StudentStatus.Active, TargetStatus = StudentStatus.Suspended, Reason = "测试学籍异动流程", State = state }; } private sealed class TestDataScope( Guid? collegeId, DataScope dataScope, string role) : ICurrentUserDataScope { public CurrentUserScope Current { get; } = new( Guid.NewGuid(), "测试管理员", collegeId, dataScope, new HashSet { role }); } }