Files
chapter-organizer/Tests/YearTransition/YearTransitionPlanner_Tests.cs
poprhythmandCursor 29101e2ead feat: add optional student nickname for informal display
Keep legal first and last names for formal lists; show DisplayFirstName on teams, calendars, and import matching so two Josiahs can be told apart.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-12 00:06:33 -04:00

257 lines
9.1 KiB
C#

using Core.Entities;
using Core.Models;
using Core.YearTransition;
using NUnit.Framework;
using Tests.Builders;
namespace Tests.YearTransition;
[TestFixture]
public class GraduatingGradeResolver_Tests
{
[Test]
public void FromSchoolLevel_MiddleSchool_Returns8()
{
Assert.That(GraduatingGradeResolver.FromSchoolLevel(SchoolLevel.MiddleSchool), Is.EqualTo(8));
}
[Test]
public void FromSchoolLevel_HighSchool_Returns12()
{
Assert.That(GraduatingGradeResolver.FromSchoolLevel(SchoolLevel.HighSchool), Is.EqualTo(12));
}
[Test]
public void FromSchoolLevel_Null_ReturnsNull()
{
Assert.That(GraduatingGradeResolver.FromSchoolLevel(null), Is.Null);
}
}
[TestFixture]
public class YearTransitionPlanner_Tests
{
[SetUp]
public void SetUp()
{
StudentBuilder.ResetIdCounter();
}
[Test]
public void SuggestReturning_BelowGraduatingGrade_IsTrue()
{
var student = StudentBuilder.Create("Ann", "Lee").WithGrade(7).Build();
Assert.That(YearTransitionPlanner.SuggestReturning(student, graduatingGrade: 8), Is.True);
}
[Test]
public void SuggestReturning_AtGraduatingGrade_IsFalse()
{
var student = StudentBuilder.Create("Ann", "Lee").WithGrade(8).Build();
Assert.That(YearTransitionPlanner.SuggestReturning(student, graduatingGrade: 8), Is.False);
}
[Test]
public void Build_PromotesReturningStudents_MiddleSchool()
{
var returning = StudentBuilder.Create("Ann", "Lee").WithGrade(6).WithTsaYear(1).Build();
var graduating = StudentBuilder.Create("Bob", "Smith").WithGrade(8).WithTsaYear(3).Build();
var plan = YearTransitionPlanner.Build(new YearTransitionRequest
{
Students = [returning, graduating],
ReturningStudentIds = new HashSet<int> { returning.Id },
GraduatingGrade = 8,
TargetCompetitionYear = "2027"
});
Assert.That(plan.ReturningCount, Is.EqualTo(1));
Assert.That(plan.RemovalCount, Is.EqualTo(1));
Assert.That(plan.Promotions[0].NewGrade, Is.EqualTo(7));
Assert.That(plan.Promotions[0].NewTsaYear, Is.EqualTo(2));
Assert.That(plan.StudentsToRemove[0].Id, Is.EqualTo(graduating.Id));
}
[Test]
public void Build_CapsGradeAtGraduatingGrade_HighSchool()
{
var senior = StudentBuilder.Create("Chris", "Young").WithGrade(12).WithTsaYear(4).Build();
var plan = YearTransitionPlanner.Build(new YearTransitionRequest
{
Students = [senior],
ReturningStudentIds = new HashSet<int> { senior.Id },
GraduatingGrade = 12,
TargetCompetitionYear = "2027"
});
Assert.That(plan.Promotions[0].NewGrade, Is.EqualTo(12));
Assert.That(plan.Promotions[0].NewTsaYear, Is.EqualTo(5));
Assert.That(plan.Warnings, Has.Some.Contain("graduating grade"));
}
[Test]
public void Build_CapsGradeAtEight_WhenMiddleSchoolReturnerAtGraduatingGrade()
{
var eighth = StudentBuilder.Create("Dana", "Nguyen").WithGrade(8).WithTsaYear(2).Build();
var plan = YearTransitionPlanner.Build(new YearTransitionRequest
{
Students = [eighth],
ReturningStudentIds = new HashSet<int> { eighth.Id },
GraduatingGrade = 8,
TargetCompetitionYear = "2027"
});
Assert.That(plan.Promotions[0].NewGrade, Is.EqualTo(8));
Assert.That(plan.Warnings, Has.Some.Contain("graduating grade"));
}
[Test]
public void Build_AssignsOfficersAndClearsOthers()
{
var president = StudentBuilder.Create("Eve", "Adams").WithGrade(7).AsPresident().Build();
var vp = StudentBuilder.Create("Frank", "Baker").WithGrade(6).Build();
var plan = YearTransitionPlanner.Build(new YearTransitionRequest
{
Students = [president, vp],
ReturningStudentIds = new HashSet<int> { president.Id, vp.Id },
GraduatingGrade = 8,
TargetCompetitionYear = "2027",
OfficerAssignments = new Dictionary<OfficerRole, int?>
{
[OfficerRole.President] = vp.Id,
[OfficerRole.VicePresident] = president.Id
}
});
var eve = plan.Promotions.Single(p => p.Student.Id == president.Id);
var frank = plan.Promotions.Single(p => p.Student.Id == vp.Id);
Assert.That(eve.PreviousOfficerRole, Is.EqualTo(OfficerRole.President));
Assert.That(eve.NewOfficerRole, Is.EqualTo(OfficerRole.VicePresident));
Assert.That(frank.NewOfficerRole, Is.EqualTo(OfficerRole.President));
var presidentChange = plan.OfficerChanges.Single(c => c.Role == OfficerRole.President);
Assert.That(presidentChange.PreviousOfficer!.Id, Is.EqualTo(president.Id));
Assert.That(presidentChange.NewOfficer!.Id, Is.EqualTo(vp.Id));
}
[Test]
public void Build_WarnsWhenOfficerAssignedToNonReturningStudent()
{
var returning = StudentBuilder.Create("Gina", "Cole").WithGrade(6).Build();
var leaving = StudentBuilder.Create("Hank", "Diaz").WithGrade(8).Build();
var plan = YearTransitionPlanner.Build(new YearTransitionRequest
{
Students = [returning, leaving],
ReturningStudentIds = new HashSet<int> { returning.Id },
GraduatingGrade = 8,
TargetCompetitionYear = "2027",
OfficerAssignments = new Dictionary<OfficerRole, int?>
{
[OfficerRole.President] = leaving.Id
}
});
Assert.That(plan.Warnings, Has.Some.Contain("not marked returning"));
Assert.That(plan.Promotions[0].NewOfficerRole, Is.Null);
}
[Test]
public void Build_WarnsWhenSameStudentHasTwoOffices()
{
var student = StudentBuilder.Create("Ivy", "Evans").WithGrade(7).Build();
var plan = YearTransitionPlanner.Build(new YearTransitionRequest
{
Students = [student],
ReturningStudentIds = new HashSet<int> { student.Id },
GraduatingGrade = 8,
TargetCompetitionYear = "2027",
OfficerAssignments = new Dictionary<OfficerRole, int?>
{
[OfficerRole.President] = student.Id,
[OfficerRole.Treasurer] = student.Id
}
});
Assert.That(plan.Warnings, Has.Some.Contain("more than one officer role"));
Assert.That(plan.Promotions[0].NewOfficerRole, Is.Null);
}
[Test]
public void MatchPastedNames_MatchesLastCommaFirstAndFirstLast()
{
var a = StudentBuilder.Create("Ann", "Lee").WithGrade(6).Build();
var b = StudentBuilder.Create("Bob", "Smith").WithGrade(7).Build();
var result = YearTransitionPlanner.MatchPastedNames(
[a, b],
["Lee, Ann", "Bob Smith", "Nobody Here"]);
Assert.That(result.MatchedStudentIds, Is.EquivalentTo(new[] { a.Id, b.Id }));
Assert.That(result.UnmatchedNames, Is.EquivalentTo(new[] { "Nobody Here" }));
}
[Test]
public void MatchPastedNames_MatchesNicknamePlusLastName()
{
var josiah = StudentBuilder.Create("Josiah", "Brown").WithNickname("Jo").WithGrade(7).Build();
var result = YearTransitionPlanner.MatchPastedNames(
[josiah],
["Jo Brown", "Brown, Jo"]);
Assert.That(result.MatchedStudentIds, Is.EquivalentTo(new[] { josiah.Id }));
Assert.That(result.UnmatchedNames, Is.Empty);
}
[Test]
public void MatchPastedNames_LegalNameStillMatchesWhenNicknameSet()
{
var josiah = StudentBuilder.Create("Josiah", "Brown").WithNickname("Jo").WithGrade(7).Build();
var result = YearTransitionPlanner.MatchPastedNames(
[josiah],
["Josiah Brown", "Brown, Josiah"]);
Assert.That(result.MatchedStudentIds, Is.EquivalentTo(new[] { josiah.Id }));
Assert.That(result.UnmatchedNames, Is.Empty);
}
[Test]
public void MatchPastedNames_AmbiguousWhenDuplicateNames()
{
var a = StudentBuilder.Create("Ann", "Lee").WithGrade(6).Build();
var b = StudentBuilder.Create("Ann", "Lee").WithGrade(7).Build();
var result = YearTransitionPlanner.MatchPastedNames([a, b], ["Ann Lee"]);
Assert.That(result.AmbiguousNames, Is.EquivalentTo(new[] { "Ann Lee" }));
Assert.That(result.MatchedStudentIds, Is.EquivalentTo(new[] { a.Id, b.Id }));
}
[Test]
public void Build_IncludesUnmatchedAndAmbiguousFromPaste()
{
var a = StudentBuilder.Create("Ann", "Lee").WithGrade(6).Build();
var b = StudentBuilder.Create("Ann", "Lee").WithGrade(7).Build();
var plan = YearTransitionPlanner.Build(new YearTransitionRequest
{
Students = [a, b],
ReturningStudentIds = new HashSet<int> { a.Id },
GraduatingGrade = 8,
TargetCompetitionYear = "2027",
PastedNames = ["Ann Lee", "Zed Zulu"]
});
Assert.That(plan.AmbiguousPastedNames, Has.Member("Ann Lee"));
Assert.That(plan.UnmatchedPastedNames, Has.Member("Zed Zulu"));
Assert.That(plan.Warnings, Has.Some.Contain("matched more than one"));
}
}