using Jiaowu.Api.Domain.Academic; using Jiaowu.Api.Infrastructure.Graduation; namespace Jiaowu.Api.Tests; public sealed class GraduationAuditRulesTests { [Fact] public void Curriculum_groups_Accept_Any_Options_That_Fulfill_Required_Credits() { var football = new Course { Code = "PE101", Name = "足球", Credits = 1 }; var basketball = new Course { Code = "PE102", Name = "篮球", Credits = 1 }; var swimming = new Course { Code = "PE103", Name = "游泳", Credits = 1 }; var module = new CurriculumModule { Code = "SPORTS", Name = "大学体育", RequiredCredits = 2, Courses = [Option(football), Option(basketball), Option(swimming)] }; var completed = CurriculumCompletionRules.Evaluate( [module], new HashSet([football.Id, swimming.Id])); var incomplete = CurriculumCompletionRules.Evaluate( [module], new HashSet([football.Id])); Assert.Equal(1, completed.RequirementCount); Assert.Equal(1, completed.PassedRequirementCount); Assert.Empty(completed.MissingRequirements); Assert.Equal(0, incomplete.PassedRequirementCount); Assert.Contains("大学体育(还差 1 学分)", incomplete.MissingRequirements); } [Fact] public void Curriculum_groups_Still_Require_Every_Fixed_Course() { var ideology = new Course { Code = "MORAL101", Name = "思想道德与法治", Credits = 3 }; var module = new CurriculumModule { Code = "MORAL", Name = "德育课程", RequiredCredits = 3, Courses = [ new CurriculumCourse { CourseId = ideology.Id, Course = ideology, Type = CurriculumCourseType.Required } ] }; var result = CurriculumCompletionRules.Evaluate([module], new HashSet()); Assert.Equal(1, result.RequirementCount); Assert.Equal(0, result.PassedRequirementCount); Assert.Contains("思想道德与法治", result.MissingRequirements); } [Fact] public void Active_student_with_full_credits_and_courses_is_eligible() { var conclusion = GraduationAuditRules.Evaluate( true, StudentStatus.Active, 160, 162, 42, 42, 0); Assert.Equal(GraduationAuditConclusion.Eligible, conclusion); } [Theory] [InlineData(false, StudentStatus.Active, 160, 162, 42, 42, 0)] [InlineData(true, StudentStatus.Suspended, 160, 162, 42, 42, 0)] [InlineData(true, StudentStatus.Active, 160, 159, 42, 42, 0)] [InlineData(true, StudentStatus.Active, 160, 162, 42, 41, 0)] [InlineData(true, StudentStatus.Active, 160, 162, 42, 42, 1)] public void Any_unresolved_requirement_makes_student_ineligible( bool hasPlan, StudentStatus status, decimal requiredCredits, decimal earnedCredits, int requiredCourses, int passedRequiredCourses, int failedCourses) { var conclusion = GraduationAuditRules.Evaluate( hasPlan, status, requiredCredits, earnedCredits, requiredCourses, passedRequiredCourses, failedCourses); Assert.Equal(GraduationAuditConclusion.Ineligible, conclusion); } private static CurriculumCourse Option(Course course) => new() { CourseId = course.Id, Course = course, Type = CurriculumCourseType.Elective }; }