using Core.Entities; using Core.Validation; using Core.Validation.Rules.TeamRules; using Tests.Builders; namespace Tests.Validation.Rules; [TestFixture] public class TeamRulesTests { [SetUp] public void SetUp() { BuilderExtensions.ResetAllBuilders(); } #region MissingCaptainRule Tests [Test] public void MissingCaptainRule_TeamHasCaptain_ReturnsNull() { // Arrange var config = new ValidationConfiguration { RequireTeamCaptain = true }; var rule = new MissingCaptainRule(); var robotics = EventDefinitionBuilder.Team("Robotics", 2, 4).Build(); var captain = StudentBuilder.Default().WithName("Alice", "A").Build(); var member = StudentBuilder.Default().WithName("Bob", "B").Build(); var team = TeamBuilder.Default() .ForEvent(robotics) .WithStudent(captain, isCaptain: true) .WithStudent(member) .Build(); // Act var warning = rule.Validate(team, config); // Assert Assert.That(warning, Is.Null); } [Test] public void MissingCaptainRule_TeamLacksCaptain_ReturnsWarning() { // Arrange var config = new ValidationConfiguration { RequireTeamCaptain = true, MissingCaptainSeverity = ValidationSeverity.Warning }; var rule = new MissingCaptainRule(); var robotics = EventDefinitionBuilder.Team("Robotics", 2, 4).Build(); var team = TeamBuilder.Default() .ForEvent(robotics) .WithStudent(StudentBuilder.Default().WithName("Alice", "A").Build()) .WithStudent(StudentBuilder.Default().WithName("Bob", "B").Build()) .Build(); // Act var warning = rule.Validate(team, config); // Assert Assert.That(warning, Is.Not.Null); Assert.That(warning!.Code, Is.EqualTo("MISSING_CAPTAIN")); Assert.That(warning.Message, Does.Contain("no captain assigned")); Assert.That(warning.Severity, Is.EqualTo(ValidationSeverity.Warning)); Assert.That(warning.IconIdentifier, Is.EqualTo("Captain")); } [Test] public void MissingCaptainRule_RequirementDisabled_ReturnsNull() { // Arrange var config = new ValidationConfiguration { RequireTeamCaptain = false }; var rule = new MissingCaptainRule(); var robotics = EventDefinitionBuilder.Team("Robotics", 2, 4).Build(); var team = TeamBuilder.Default() .ForEvent(robotics) .WithStudent(StudentBuilder.Default().Build()) .Build(); // Act var warning = rule.Validate(team, config); // Assert Assert.That(warning, Is.Null); } [Test] public void MissingCaptainRule_IndividualEvent_ReturnsNull() { // Arrange var config = new ValidationConfiguration { RequireTeamCaptain = true }; var rule = new MissingCaptainRule(); var flight = EventDefinitionBuilder.Individual("Flight").Build(); var team = TeamBuilder.Default() .ForEvent(flight) .WithStudent(StudentBuilder.Default().Build()) .Build(); // Act var warning = rule.Validate(team, config); // Assert Assert.That(warning, Is.Null, "Individual events should not require a captain"); } #endregion #region TeamSizeTooSmallRule Tests [Test] public void TeamSizeTooSmallRule_MeetsMinimum_ReturnsNull() { // Arrange var config = new ValidationConfiguration { TeamSizeSeverity = ValidationSeverity.Warning }; var rule = new TeamSizeTooSmallRule(); var robotics = EventDefinitionBuilder.Team("Robotics", 2, 4).Build(); var team = TeamBuilder.Default() .ForEvent(robotics) .WithStudent(StudentBuilder.Default().WithName("Alice", "A").Build()) .WithStudent(StudentBuilder.Default().WithName("Bob", "B").Build()) .Build(); // Act var warning = rule.Validate(team, config); // Assert Assert.That(warning, Is.Null); } [Test] public void TeamSizeTooSmallRule_BelowMinimum_ReturnsWarning() { // Arrange var config = new ValidationConfiguration { TeamSizeSeverity = ValidationSeverity.Error }; var rule = new TeamSizeTooSmallRule(); var robotics = EventDefinitionBuilder.Team("Robotics", 3, 5).Build(); var team = TeamBuilder.Default() .ForEvent(robotics) .WithStudent(StudentBuilder.Default().WithName("Alice", "A").Build()) .Build(); // Act var warning = rule.Validate(team, config); // Assert Assert.That(warning, Is.Not.Null); Assert.That(warning!.Code, Is.EqualTo("TEAM_SIZE_TOO_SMALL")); Assert.That(warning.Message, Does.Contain("1 members")); Assert.That(warning.Message, Does.Contain("min: 3")); Assert.That(warning.Severity, Is.EqualTo(ValidationSeverity.Error)); } [Test] public void TeamSizeTooSmallRule_IndividualEvent_ReturnsNull() { // Arrange var config = new ValidationConfiguration { TeamSizeSeverity = ValidationSeverity.Warning }; var rule = new TeamSizeTooSmallRule(); var flight = EventDefinitionBuilder.Individual("Flight").Build(); var team = TeamBuilder.Default() .ForEvent(flight) .WithStudent(StudentBuilder.Default().Build()) .Build(); // Act var warning = rule.Validate(team, config); // Assert Assert.That(warning, Is.Null); } [Test] public void TeamSizeTooSmallRule_ContainsMetadata() { // Arrange var config = new ValidationConfiguration { TeamSizeSeverity = ValidationSeverity.Warning }; var rule = new TeamSizeTooSmallRule(); var robotics = EventDefinitionBuilder.Team("Robotics", 4, 6).Build(); var team = TeamBuilder.Default() .ForEvent(robotics) .WithStudent(StudentBuilder.Default().Build()) .WithStudent(StudentBuilder.Default().Build()) .Build(); // Act var warning = rule.Validate(team, config); // Assert Assert.That(warning, Is.Not.Null); Assert.That(warning!.Metadata, Contains.Key("EventName")); Assert.That(warning.Metadata, Contains.Key("ActualSize")); Assert.That(warning.Metadata, Contains.Key("MinSize")); Assert.That(warning.Metadata["ActualSize"], Is.EqualTo(2)); Assert.That(warning.Metadata["MinSize"], Is.EqualTo(4)); } #endregion #region TeamSizeTooLargeRule Tests [Test] public void TeamSizeTooLargeRule_MeetsMaximum_ReturnsNull() { // Arrange var config = new ValidationConfiguration { TeamSizeSeverity = ValidationSeverity.Warning }; var rule = new TeamSizeTooLargeRule(); var robotics = EventDefinitionBuilder.Team("Robotics", 2, 4).Build(); var team = TeamBuilder.Default() .ForEvent(robotics) .WithStudent(StudentBuilder.Default().WithName("Alice", "A").Build()) .WithStudent(StudentBuilder.Default().WithName("Bob", "B").Build()) .WithStudent(StudentBuilder.Default().WithName("Carol", "C").Build()) .Build(); // Act var warning = rule.Validate(team, config); // Assert Assert.That(warning, Is.Null); } [Test] public void TeamSizeTooLargeRule_ExceedsMaximum_ReturnsWarning() { // Arrange var config = new ValidationConfiguration { TeamSizeSeverity = ValidationSeverity.Warning }; var rule = new TeamSizeTooLargeRule(); var robotics = EventDefinitionBuilder.Team("Robotics", 2, 3).Build(); var team = TeamBuilder.Default() .ForEvent(robotics) .WithStudent(StudentBuilder.Default().WithName("Alice", "A").Build()) .WithStudent(StudentBuilder.Default().WithName("Bob", "B").Build()) .WithStudent(StudentBuilder.Default().WithName("Carol", "C").Build()) .WithStudent(StudentBuilder.Default().WithName("David", "D").Build()) .WithStudent(StudentBuilder.Default().WithName("Eve", "E").Build()) .Build(); // Act var warning = rule.Validate(team, config); // Assert Assert.That(warning, Is.Not.Null); Assert.That(warning!.Code, Is.EqualTo("TEAM_SIZE_TOO_LARGE")); Assert.That(warning.Message, Does.Contain("5 members")); Assert.That(warning.Message, Does.Contain("max: 3")); } [Test] public void TeamSizeTooLargeRule_IndividualEvent_ReturnsNull() { // Arrange var config = new ValidationConfiguration { TeamSizeSeverity = ValidationSeverity.Warning }; var rule = new TeamSizeTooLargeRule(); var flight = EventDefinitionBuilder.Individual("Flight").Build(); var team = TeamBuilder.Default() .ForEvent(flight) .WithStudent(StudentBuilder.Default().Build()) .Build(); // Act var warning = rule.Validate(team, config); // Assert Assert.That(warning, Is.Null); } #endregion #region AppliesTo Tests [Test] public void TeamRules_ApplyToCorrectContexts() { // Arrange var rules = new IValidationRule[] { new MissingCaptainRule(), new TeamSizeTooSmallRule(), new TeamSizeTooLargeRule() }; // Act & Assert - all team rules should apply to Team context only foreach (var rule in rules) { Assert.That(rule.AppliesTo(ValidationContext.Team), Is.True, $"{rule.GetType().Name} should apply to Team"); Assert.That(rule.AppliesTo(ValidationContext.StudentRanking), Is.False, $"{rule.GetType().Name} should not apply to StudentRanking"); Assert.That(rule.AppliesTo(ValidationContext.StudentAssignment), Is.False, $"{rule.GetType().Name} should not apply to StudentAssignment"); Assert.That(rule.AppliesTo(ValidationContext.StudentRegistration), Is.False, $"{rule.GetType().Name} should not apply to StudentRegistration"); } } #endregion }