Files
Academic-Affairs-System/tests/Jiaowu.Api.Tests/CourseAdjustmentsControllerTests.cs
2026-07-27 08:59:02 +08:00

275 lines
9.1 KiB
C#

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 CourseAdjustmentsControllerTests
{
[Fact]
public async Task Reschedule_MovesOnlyTheSelectedOccurrence()
{
await using var fixture = await Fixture.CreateAsync();
var teacherController = new CourseAdjustmentsController(
fixture.Db,
new TestScope(fixture.TeacherUserId, DataScope.Self, SystemRoles.Teacher));
var targetDate = new DateOnly(2026, 9, 24);
var created = await teacherController.Create(
new CourseAdjustmentRequest(
fixture.Task.Id,
CourseAdjustmentType.Reschedule,
"参加学院教研活动",
true,
targetDate,
null,
3,
null,
fixture.Classroom.Id,
null,
null,
null,
fixture.Entry.Id,
3),
CancellationToken.None);
Assert.IsType<CreatedResult>(created);
var adjustment = await fixture.Db.CourseAdjustments.SingleAsync();
Assert.Equal(new DateOnly(2026, 9, 16), adjustment.SourceDate);
Assert.Equal(1, adjustment.SourceStartPeriod);
Assert.Equal(2, adjustment.SourcePeriodCount);
var adminController = new CourseAdjustmentsController(
fixture.Db,
new TestScope(Guid.NewGuid(), DataScope.All, SystemRoles.SuperAdmin));
var approved = await adminController.Approve(
adjustment.Id,
CancellationToken.None);
Assert.IsType<NoContentResult>(approved);
var entries = await fixture.Db.ScheduleEntries
.OrderBy(x => x.StartWeek)
.ThenBy(x => x.DayOfWeek)
.ToListAsync();
Assert.Contains(entries, x =>
x.DayOfWeek == 3 && x.StartWeek == 1 && x.EndWeek == 2);
Assert.Contains(entries, x =>
x.DayOfWeek == 3 && x.StartWeek == 4 && x.EndWeek == 5);
Assert.Contains(entries, x =>
x.DayOfWeek == 4 && x.StartWeek == 4 && x.EndWeek == 4 &&
x.StartPeriod == 3 && x.PeriodCount == 2);
Assert.DoesNotContain(entries, x =>
x.DayOfWeek == 3 && x.StartWeek <= 3 && x.EndWeek >= 3);
}
[Fact]
public async Task Cancel_RemovesOnlyTheSelectedMiddleWeek()
{
await using var fixture = await Fixture.CreateAsync();
var teacherController = new CourseAdjustmentsController(
fixture.Db,
new TestScope(fixture.TeacherUserId, DataScope.Self, SystemRoles.Teacher));
await teacherController.Create(
new CourseAdjustmentRequest(
fixture.Task.Id,
CourseAdjustmentType.Cancel,
"参加培训",
true,
null,
null,
null,
null,
null,
null,
null,
null,
fixture.Entry.Id,
3),
CancellationToken.None);
var adjustment = await fixture.Db.CourseAdjustments.SingleAsync();
var adminController = new CourseAdjustmentsController(
fixture.Db,
new TestScope(Guid.NewGuid(), DataScope.All, SystemRoles.SuperAdmin));
var approved = await adminController.Approve(
adjustment.Id,
CancellationToken.None);
Assert.IsType<NoContentResult>(approved);
var entries = await fixture.Db.ScheduleEntries
.OrderBy(x => x.StartWeek)
.ToListAsync();
Assert.Equal(2, entries.Count);
Assert.Contains(entries, x => x.StartWeek == 1 && x.EndWeek == 2);
Assert.Contains(entries, x => x.StartWeek == 4 && x.EndWeek == 5);
}
private sealed class Fixture : IAsyncDisposable
{
private readonly SqliteConnection connection;
private Fixture(
SqliteConnection connection,
AppDbContext db,
Guid teacherUserId,
TeachingTask task,
ScheduleEntry entry,
Classroom classroom)
{
this.connection = connection;
Db = db;
TeacherUserId = teacherUserId;
Task = task;
Entry = entry;
Classroom = classroom;
}
public AppDbContext Db { get; }
public Guid TeacherUserId { get; }
public TeachingTask Task { get; }
public ScheduleEntry Entry { get; }
public Classroom Classroom { get; }
public static async Task<Fixture> CreateAsync()
{
var connection = new SqliteConnection("Data Source=:memory:");
await connection.OpenAsync();
var db = new AppDbContext(new DbContextOptionsBuilder<AppDbContext>()
.UseSqlite(connection)
.Options);
await db.Database.EnsureCreatedAsync();
var teacherUserId = Guid.NewGuid();
var user = new ApplicationUser
{
Id = teacherUserId,
UserName = "T001",
NormalizedUserName = "T001",
DisplayName = "张老师"
};
var campus = new Campus { Code = "MAIN", Name = "主校区" };
var college = new College
{
Code = "CS",
Name = "计算机学院",
CampusId = campus.Id
};
var building = new Building
{
Code = "A",
Name = "教学楼 A",
CampusId = campus.Id
};
var classroom = new Classroom
{
Code = "A101",
Name = "A101",
BuildingId = building.Id,
Capacity = 60
};
var teacher = new Teacher
{
TeacherNumber = "T001",
Name = "张老师",
CollegeId = college.Id,
UserId = teacherUserId
};
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 task = new TeachingTask
{
TaskNumber = "2026-1-CS101-01",
Name = "程序设计基础教学班",
AcademicTermId = term.Id,
CourseId = course.Id,
Capacity = 60,
Status = TeachingTaskStatus.Published,
Teachers =
[
new TeachingTaskTeacher
{
TeacherId = teacher.Id,
IsPrimary = true
}
]
};
var plan = new SchedulePlan
{
AcademicTermId = term.Id,
Name = "正式课表",
Version = "1.0",
Status = SchedulePlanStatus.Published
};
var entry = new ScheduleEntry
{
SchedulePlanId = plan.Id,
TeachingTaskId = task.Id,
ClassroomId = classroom.Id,
DayOfWeek = 3,
StartPeriod = 1,
PeriodCount = 2,
StartWeek = 1,
EndWeek = 5,
WeekPattern = WeekPattern.All
};
db.AddRange(
user,
campus,
college,
building,
classroom,
teacher,
course,
term,
task,
plan,
entry);
await db.SaveChangesAsync();
return new Fixture(connection, db, teacherUserId, task, entry, classroom);
}
public async ValueTask DisposeAsync()
{
await Db.DisposeAsync();
await connection.DisposeAsync();
}
}
private sealed class TestScope(
Guid userId,
DataScope scope,
string role) : ICurrentUserDataScope
{
public CurrentUserScope Current { get; } = new(
userId,
"测试用户",
null,
scope,
new HashSet<string>([role]));
}
}