using System.Text.Json; using Jiaowu.Api.Contracts; 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.Persistence; using Microsoft.AspNetCore.Mvc; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; namespace Jiaowu.Api.Tests; public sealed class CoursesControllerTests { [Fact] public async Task CollegeAdmin_GetWithoutCollegeFilter_ReturnsOnlyOwnCollegeCourses() { await using var connection = new SqliteConnection("Data Source=:memory:"); await connection.OpenAsync(); var options = new DbContextOptionsBuilder() .UseSqlite(connection) .Options; await using var db = new AppDbContext(options); await db.Database.EnsureCreatedAsync(); var ownCollege = new College { Code = "OWN", Name = "本院" }; var otherCollege = new College { Code = "OTHER", Name = "外院" }; var category = new CourseCategory { Code = "CORE", Name = "核心课程" }; db.AddRange( ownCollege, otherCollege, category, new Course { Code = "OWN-001", Name = "本院课程", CollegeId = ownCollege.Id, CourseCategoryId = category.Id, Nature = CourseNature.MajorRequired, Credits = 2, TotalHours = 32, IsEnabled = true }, new Course { Code = "OTHER-001", Name = "外院通识课程", CollegeId = otherCollege.Id, CourseCategoryId = category.Id, Nature = CourseNature.GeneralRequired, Credits = 2, TotalHours = 32, IsEnabled = true }); await db.SaveChangesAsync(); var scope = new TestDataScope(ownCollege.Id); var controller = new CoursesController(db, scope, NoOpAppCache.Instance); var result = await controller.Get(cancellationToken: CancellationToken.None); var ok = Assert.IsType(result.Result); var page = Assert.IsType>(ok.Value); Assert.Equal(1, page.Total); var items = JsonSerializer.Serialize(page.Items); Assert.Contains("OWN-001", items); Assert.DoesNotContain("OTHER-001", items); } [Fact] public async Task CollegeAdmin_CurriculumCourseOptions_IncludeEnabledCoursesFromOtherColleges() { await using var connection = new SqliteConnection("Data Source=:memory:"); await connection.OpenAsync(); var options = new DbContextOptionsBuilder() .UseSqlite(connection) .Options; await using var db = new AppDbContext(options); await db.Database.EnsureCreatedAsync(); var ownCollege = new College { Code = "OWN", Name = "本院" }; var otherCollege = new College { Code = "OTHER", Name = "外院" }; var category = new CourseCategory { Code = "CORE", Name = "核心课程" }; db.AddRange( ownCollege, otherCollege, category, new Course { Code = "OWN-001", Name = "本院课程", CollegeId = ownCollege.Id, CourseCategoryId = category.Id, Nature = CourseNature.MajorRequired, Credits = 2, TotalHours = 32, IsEnabled = true }, new Course { Code = "OTHER-001", Name = "外院课程", CollegeId = otherCollege.Id, CourseCategoryId = category.Id, Nature = CourseNature.MajorRequired, Credits = 2, TotalHours = 32, IsEnabled = true }); await db.SaveChangesAsync(); var controller = new CurriculumPlansController(db, new TestDataScope(ownCollege.Id)); var result = await controller.GetCourseOptions(CancellationToken.None); var ok = Assert.IsType(result); var courses = JsonSerializer.Serialize(ok.Value); Assert.Contains("OWN-001", courses); Assert.Contains("OTHER-001", courses); } [Fact] public async Task Student_Get_IncludesCourseFromActivePersonalEnrollment() { await using var connection = new SqliteConnection("Data Source=:memory:"); await connection.OpenAsync(); var options = new DbContextOptionsBuilder() .UseSqlite(connection) .Options; await using var db = new AppDbContext(options); await db.Database.EnsureCreatedAsync(); var studentUserId = Guid.NewGuid(); var college = new College { Code = "CS", Name = "计算机学院" }; var major = new Major { Code = "CS01", Name = "软件工程", CollegeId = college.Id, DegreeType = "本科" }; var administrativeClass = new AdministrativeClass { Code = "SE2601", Name = "软件 2601", MajorId = major.Id, Grade = 2026 }; var student = new Student { StudentNumber = "20260001", Name = "测试学生", UserId = studentUserId, AdministrativeClassId = administrativeClass.Id, EnrollmentDate = new DateOnly(2026, 9, 1) }; var category = new CourseCategory { Code = "CORE", Name = "核心课程" }; var selectedCourse = new Course { Code = "CS102", Name = "数据结构", CollegeId = college.Id, CourseCategoryId = category.Id, Nature = CourseNature.MajorElective, Credits = 3, TotalHours = 48, IsEnabled = true }; var term = new AcademicTerm { Code = "2026-1", Name = "2026-2027-1", AcademicYear = "2026-2027", Season = TermSeason.Autumn, StartDate = new DateOnly(2026, 9, 1), EndDate = new DateOnly(2027, 1, 31), IsCurrent = true }; var task = new TeachingTask { TaskNumber = "T-CS102", Name = "数据结构选课班", AcademicTermId = term.Id, CourseId = selectedCourse.Id, Capacity = 50, Status = TeachingTaskStatus.Published }; var round = new CourseSelectionRound { AcademicTermId = term.Id, Name = "第一轮", StartsAt = DateTime.UtcNow, EndsAt = DateTime.UtcNow.AddDays(1), WithdrawalEndsAt = DateTime.UtcNow.AddDays(2) }; var offering = new CourseSelectionOffering { CourseSelectionRoundId = round.Id, TeachingTaskId = task.Id, Capacity = 50 }; db.AddRange(college, major, administrativeClass, student, category, selectedCourse, new ApplicationUser { Id = studentUserId, UserName = "20260001", DisplayName = "测试学生" }, term, task, round, offering, new CourseEnrollment { CourseSelectionOfferingId = offering.Id, StudentId = student.Id, Status = CourseEnrollmentStatus.Enrolled }); await db.SaveChangesAsync(); var controller = new CoursesController( db, new StudentDataScope(studentUserId), NoOpAppCache.Instance); var result = await controller.Get(cancellationToken: CancellationToken.None); var ok = Assert.IsType(result.Result); var page = Assert.IsType>(ok.Value); Assert.Equal(1, page.Total); Assert.Contains("CS102", JsonSerializer.Serialize(page.Items)); } private sealed class TestDataScope(Guid collegeId) : ICurrentUserDataScope { public CurrentUserScope Current { get; } = new( Guid.NewGuid(), "学院管理员", collegeId, DataScope.College, new HashSet([SystemRoles.CollegeAdmin])); } private sealed class StudentDataScope(Guid userId) : ICurrentUserDataScope { public CurrentUserScope Current { get; } = new( userId, "测试学生", null, DataScope.Self, new HashSet([SystemRoles.Student])); } }