204 lines
8.1 KiB
C#
204 lines
8.1 KiB
C#
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<AppDbContext>()
|
|
.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<OkObjectResult>(result.Result);
|
|
var page = Assert.IsType<CursorPagedResult<object>>(ok.Value);
|
|
Assert.Equal(1, page.Total);
|
|
Assert.False(page.HasMore);
|
|
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<AppDbContext>()
|
|
.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<OkObjectResult>(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<AppDbContext>()
|
|
.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<OkObjectResult>(result.Result);
|
|
var page = Assert.IsType<CursorPagedResult<object>>(ok.Value);
|
|
Assert.Equal(1, page.Total);
|
|
Assert.False(page.HasMore);
|
|
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<string>([SystemRoles.CollegeAdmin]));
|
|
}
|
|
|
|
private sealed class StudentDataScope(Guid userId) : ICurrentUserDataScope
|
|
{
|
|
public CurrentUserScope Current { get; } = new(
|
|
userId,
|
|
"测试学生",
|
|
null,
|
|
DataScope.Self,
|
|
new HashSet<string>([SystemRoles.Student]));
|
|
}
|
|
}
|