Add testing for validation
This commit is contained in:
@@ -0,0 +1,440 @@
|
||||
using Core.Entities;
|
||||
using Core.Validation;
|
||||
using Core.Validation.Rules.StudentAssignmentRules;
|
||||
using Tests.Builders;
|
||||
|
||||
namespace Tests.Validation.Rules;
|
||||
|
||||
[TestFixture]
|
||||
public class StudentAssignmentRulesTests
|
||||
{
|
||||
private Student _testStudent;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
BuilderExtensions.ResetAllBuilders();
|
||||
_testStudent = StudentBuilder.Default().WithName("Test", "Student").Build();
|
||||
}
|
||||
|
||||
#region TooFewEventsRule Tests
|
||||
|
||||
[Test]
|
||||
public void TooFewEventsRule_AboveMinimum_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration { MinRecommendedEvents = 2 };
|
||||
var rule = new TooFewEventsRule();
|
||||
|
||||
var stats = new StudentEventStatistics
|
||||
{
|
||||
Student = _testStudent,
|
||||
Events = new List<EventDefinition>
|
||||
{
|
||||
EventDefinitionBuilder.Individual("Flight").Build(),
|
||||
EventDefinitionBuilder.Individual("Coding").Build(),
|
||||
EventDefinitionBuilder.Individual("Speech").Build()
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(stats, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TooFewEventsRule_BelowMinimum_ReturnsWarning()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration
|
||||
{
|
||||
MinRecommendedEvents = 3,
|
||||
MinCriticalEvents = 1,
|
||||
EventCountSeverity = ValidationSeverity.Warning
|
||||
};
|
||||
var rule = new TooFewEventsRule();
|
||||
|
||||
var stats = new StudentEventStatistics
|
||||
{
|
||||
Student = _testStudent,
|
||||
Events = new List<EventDefinition>
|
||||
{
|
||||
EventDefinitionBuilder.Individual("Flight").Build()
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(stats, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Not.Null);
|
||||
Assert.That(warning!.Code, Is.EqualTo("TOO_FEW_EVENTS"));
|
||||
Assert.That(warning.Message, Does.Contain("1 events"));
|
||||
Assert.That(warning.Message, Does.Contain("min recommended: 3"));
|
||||
Assert.That(warning.Severity, Is.EqualTo(ValidationSeverity.Warning));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TooFewEventsRule_BelowCritical_EscalatesToError()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration
|
||||
{
|
||||
MinRecommendedEvents = 3,
|
||||
MinCriticalEvents = 2,
|
||||
EventCountSeverity = ValidationSeverity.Warning
|
||||
};
|
||||
var rule = new TooFewEventsRule();
|
||||
|
||||
var stats = new StudentEventStatistics
|
||||
{
|
||||
Student = _testStudent,
|
||||
Events = new List<EventDefinition>
|
||||
{
|
||||
EventDefinitionBuilder.Individual("Flight").Build()
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(stats, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Not.Null);
|
||||
Assert.That(warning!.Severity, Is.EqualTo(ValidationSeverity.Error),
|
||||
"Should escalate to Error when below critical threshold");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region TooManyEventsRule Tests
|
||||
|
||||
[Test]
|
||||
public void TooManyEventsRule_BelowMaximum_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration { MaxRecommendedEvents = 4 };
|
||||
var rule = new TooManyEventsRule();
|
||||
|
||||
var stats = new StudentEventStatistics
|
||||
{
|
||||
Student = _testStudent,
|
||||
Events = new List<EventDefinition>
|
||||
{
|
||||
EventDefinitionBuilder.Individual("Event1").Build(),
|
||||
EventDefinitionBuilder.Individual("Event2").Build(),
|
||||
EventDefinitionBuilder.Individual("Event3").Build()
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(stats, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TooManyEventsRule_AboveMaximum_ReturnsWarning()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration
|
||||
{
|
||||
MaxRecommendedEvents = 3,
|
||||
MaxCriticalEvents = 6,
|
||||
EventCountSeverity = ValidationSeverity.Warning
|
||||
};
|
||||
var rule = new TooManyEventsRule();
|
||||
|
||||
var events = new List<EventDefinition>();
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
events.Add(EventDefinitionBuilder.Individual($"Event{i}").Build());
|
||||
}
|
||||
var stats = new StudentEventStatistics
|
||||
{
|
||||
Student = _testStudent,
|
||||
Events = events
|
||||
};
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(stats, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Not.Null);
|
||||
Assert.That(warning!.Code, Is.EqualTo("TOO_MANY_EVENTS"));
|
||||
Assert.That(warning.Message, Does.Contain("5 events"));
|
||||
Assert.That(warning.Message, Does.Contain("max recommended: 3"));
|
||||
Assert.That(warning.Severity, Is.EqualTo(ValidationSeverity.Warning));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TooManyEventsRule_AboveCritical_EscalatesToError()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration
|
||||
{
|
||||
MaxRecommendedEvents = 4,
|
||||
MaxCriticalEvents = 6,
|
||||
EventCountSeverity = ValidationSeverity.Warning
|
||||
};
|
||||
var rule = new TooManyEventsRule();
|
||||
|
||||
var events = new List<EventDefinition>();
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
events.Add(EventDefinitionBuilder.Individual($"Event{i}").Build());
|
||||
}
|
||||
var stats = new StudentEventStatistics
|
||||
{
|
||||
Student = _testStudent,
|
||||
Events = events
|
||||
};
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(stats, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Not.Null);
|
||||
Assert.That(warning!.Severity, Is.EqualTo(ValidationSeverity.Error),
|
||||
"Should escalate to Error when above critical threshold");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TooManyEventsRule_ContainsMetadata()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration
|
||||
{
|
||||
MaxRecommendedEvents = 2,
|
||||
MaxCriticalEvents = 5
|
||||
};
|
||||
var rule = new TooManyEventsRule();
|
||||
|
||||
var events = new List<EventDefinition>();
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
events.Add(EventDefinitionBuilder.Individual($"Event{i}").Build());
|
||||
}
|
||||
var stats = new StudentEventStatistics
|
||||
{
|
||||
Student = _testStudent,
|
||||
Events = events
|
||||
};
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(stats, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Not.Null);
|
||||
Assert.That(warning!.Metadata, Contains.Key("MaxRecommended"));
|
||||
Assert.That(warning.Metadata, Contains.Key("MaxCritical"));
|
||||
Assert.That(warning.Metadata["MaxRecommended"], Is.EqualTo(2));
|
||||
Assert.That(warning.Metadata["MaxCritical"], Is.EqualTo(5));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region NoRegionalEventAssignmentRule Tests
|
||||
|
||||
[Test]
|
||||
public void NoRegionalEventAssignmentRule_HasRegionalEvent_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration { RequireRegionalEvent = true };
|
||||
var rule = new NoRegionalEventAssignmentRule();
|
||||
|
||||
var stats = new StudentEventStatistics
|
||||
{
|
||||
Student = _testStudent,
|
||||
Events = new List<EventDefinition>
|
||||
{
|
||||
EventDefinitionBuilder.Individual("Flight").AsRegionalEvent().Build()
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(stats, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NoRegionalEventAssignmentRule_LacksRegionalEvent_ReturnsWarning()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration
|
||||
{
|
||||
RequireRegionalEvent = true,
|
||||
NoRegionalEventSeverity = ValidationSeverity.Warning
|
||||
};
|
||||
var rule = new NoRegionalEventAssignmentRule();
|
||||
|
||||
var stats = new StudentEventStatistics
|
||||
{
|
||||
Student = _testStudent,
|
||||
Events = new List<EventDefinition>
|
||||
{
|
||||
EventDefinitionBuilder.Individual("Coding").Build()
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(stats, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Not.Null);
|
||||
Assert.That(warning!.Code, Is.EqualTo("NO_REGIONAL_EVENT_ASSIGNED"));
|
||||
Assert.That(warning.Context, Is.EqualTo(ValidationContext.StudentAssignment));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region NoOnSiteActivityAssignmentRule Tests
|
||||
|
||||
[Test]
|
||||
public void NoOnSiteActivityAssignmentRule_HasOnSiteActivity_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration { RequireOnSiteActivity = true };
|
||||
var rule = new NoOnSiteActivityAssignmentRule();
|
||||
|
||||
var stats = new StudentEventStatistics
|
||||
{
|
||||
Student = _testStudent,
|
||||
Events = new List<EventDefinition>
|
||||
{
|
||||
EventDefinitionBuilder.Individual("Speech").AsOnSite().Build()
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(stats, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NoOnSiteActivityAssignmentRule_LacksOnSiteActivity_ReturnsWarning()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration
|
||||
{
|
||||
RequireOnSiteActivity = true,
|
||||
NoOnSiteActivitySeverity = ValidationSeverity.Error
|
||||
};
|
||||
var rule = new NoOnSiteActivityAssignmentRule();
|
||||
|
||||
var stats = new StudentEventStatistics
|
||||
{
|
||||
Student = _testStudent,
|
||||
Events = new List<EventDefinition>
|
||||
{
|
||||
EventDefinitionBuilder.Individual("Flight").Build()
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(stats, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Not.Null);
|
||||
Assert.That(warning!.Code, Is.EqualTo("NO_ONSITE_ACTIVITY_ASSIGNED"));
|
||||
Assert.That(warning.Severity, Is.EqualTo(ValidationSeverity.Error));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region TooManyRegionalEventsAssignmentRule Tests
|
||||
|
||||
[Test]
|
||||
public void TooManyRegionalEventsAssignmentRule_WithinLimit_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration { MaxRegionalEvents = 3 };
|
||||
var rule = new TooManyRegionalEventsAssignmentRule();
|
||||
|
||||
var stats = new StudentEventStatistics
|
||||
{
|
||||
Student = _testStudent,
|
||||
Events = new List<EventDefinition>
|
||||
{
|
||||
EventDefinitionBuilder.Individual("Flight").AsRegionalEvent().Build(),
|
||||
EventDefinitionBuilder.Individual("Coding").AsRegionalEvent().Build()
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(stats, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TooManyRegionalEventsAssignmentRule_ExceedsLimit_ReturnsWarning()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration
|
||||
{
|
||||
MaxRegionalEvents = 2,
|
||||
TooManyRegionalEventsSeverity = ValidationSeverity.Warning
|
||||
};
|
||||
var rule = new TooManyRegionalEventsAssignmentRule();
|
||||
|
||||
var stats = new StudentEventStatistics
|
||||
{
|
||||
Student = _testStudent,
|
||||
Events = new List<EventDefinition>
|
||||
{
|
||||
EventDefinitionBuilder.Individual("Flight").AsRegionalEvent().Build(),
|
||||
EventDefinitionBuilder.Individual("Coding").AsRegionalEvent().Build(),
|
||||
EventDefinitionBuilder.Individual("Essays").AsRegionalEvent().Build()
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(stats, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Not.Null);
|
||||
Assert.That(warning!.Code, Is.EqualTo("TOO_MANY_REGIONAL_EVENTS"));
|
||||
Assert.That(warning.Message, Does.Contain("3 regional events"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region AppliesTo Tests
|
||||
|
||||
[Test]
|
||||
public void AssignmentRules_ApplyToCorrectContexts()
|
||||
{
|
||||
// Arrange
|
||||
var rules = new IValidationRule<StudentEventStatistics>[]
|
||||
{
|
||||
new TooFewEventsRule(),
|
||||
new TooManyEventsRule(),
|
||||
new NoRegionalEventAssignmentRule(),
|
||||
new NoOnSiteActivityAssignmentRule(),
|
||||
new TooManyRegionalEventsAssignmentRule()
|
||||
};
|
||||
|
||||
// Act & Assert - all assignment rules should apply to these contexts
|
||||
foreach (var rule in rules)
|
||||
{
|
||||
Assert.That(rule.AppliesTo(ValidationContext.StudentAssignment), Is.True,
|
||||
$"{rule.GetType().Name} should apply to StudentAssignment");
|
||||
Assert.That(rule.AppliesTo(ValidationContext.StudentRegistration), Is.True,
|
||||
$"{rule.GetType().Name} should apply to StudentRegistration");
|
||||
Assert.That(rule.AppliesTo(ValidationContext.StudentRanking), Is.False,
|
||||
$"{rule.GetType().Name} should not apply to StudentRanking");
|
||||
Assert.That(rule.AppliesTo(ValidationContext.Team), Is.False,
|
||||
$"{rule.GetType().Name} should not apply to Team");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
using Core.Entities;
|
||||
using Core.Validation;
|
||||
using Core.Validation.Rules.StudentRankingRules;
|
||||
using Tests.Builders;
|
||||
|
||||
namespace Tests.Validation.Rules;
|
||||
|
||||
[TestFixture]
|
||||
public class StudentRankingRulesTests
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
BuilderExtensions.ResetAllBuilders();
|
||||
}
|
||||
|
||||
#region NoRegionalEventRule Tests
|
||||
|
||||
[Test]
|
||||
public void NoRegionalEventRule_StudentHasRegionalEvent_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration
|
||||
{
|
||||
RequireRegionalEvent = true,
|
||||
RequireOnSiteActivity = false,
|
||||
RequireIndividualEvent = false
|
||||
};
|
||||
var rule = new NoRegionalEventRule();
|
||||
|
||||
var flight = EventDefinitionBuilder.Individual("Flight").AsRegionalEvent().Build();
|
||||
var student = StudentBuilder.Default()
|
||||
.WithRanking(flight, 1)
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(student, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Null, "Student with regional event should not trigger warning");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NoRegionalEventRule_StudentLacksRegionalEvent_ReturnsWarning()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration
|
||||
{
|
||||
RequireRegionalEvent = true,
|
||||
NoRegionalEventSeverity = ValidationSeverity.Warning
|
||||
};
|
||||
var rule = new NoRegionalEventRule();
|
||||
|
||||
var coding = EventDefinitionBuilder.Individual("Coding").Build(); // Not regional
|
||||
var student = StudentBuilder.Default()
|
||||
.WithRanking(coding, 1)
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(student, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Not.Null);
|
||||
Assert.That(warning!.Code, Is.EqualTo("NO_REGIONAL_EVENT"));
|
||||
Assert.That(warning.Message, Is.EqualTo("No Regional Event"));
|
||||
Assert.That(warning.Severity, Is.EqualTo(ValidationSeverity.Warning));
|
||||
Assert.That(warning.IconIdentifier, Is.EqualTo("RegionalEvent"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NoRegionalEventRule_RequirementDisabled_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration { RequireRegionalEvent = false };
|
||||
var rule = new NoRegionalEventRule();
|
||||
|
||||
var coding = EventDefinitionBuilder.Individual("Coding").Build();
|
||||
var student = StudentBuilder.Default()
|
||||
.WithRanking(coding, 1)
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(student, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Null, "Warning should not be returned when requirement is disabled");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NoRegionalEventRule_AppliesTo_CorrectContexts()
|
||||
{
|
||||
// Arrange
|
||||
var rule = new NoRegionalEventRule();
|
||||
|
||||
// Act & Assert
|
||||
Assert.That(rule.AppliesTo(ValidationContext.StudentRanking), Is.True);
|
||||
Assert.That(rule.AppliesTo(ValidationContext.StudentRegistration), Is.True);
|
||||
Assert.That(rule.AppliesTo(ValidationContext.StudentAssignment), Is.False);
|
||||
Assert.That(rule.AppliesTo(ValidationContext.Team), Is.False);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region NoOnSiteActivityRule Tests
|
||||
|
||||
[Test]
|
||||
public void NoOnSiteActivityRule_StudentHasOnSiteActivity_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration
|
||||
{
|
||||
RequireOnSiteActivity = true,
|
||||
RequireRegionalEvent = false,
|
||||
RequireIndividualEvent = false
|
||||
};
|
||||
var rule = new NoOnSiteActivityRule();
|
||||
|
||||
var speech = EventDefinitionBuilder.Individual("Speech").AsOnSite().Build();
|
||||
var student = StudentBuilder.Default()
|
||||
.WithRanking(speech, 1)
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(student, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NoOnSiteActivityRule_StudentLacksOnSiteActivity_ReturnsWarning()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration
|
||||
{
|
||||
RequireOnSiteActivity = true,
|
||||
NoOnSiteActivitySeverity = ValidationSeverity.Error
|
||||
};
|
||||
var rule = new NoOnSiteActivityRule();
|
||||
|
||||
var flight = EventDefinitionBuilder.Individual("Flight").Build(); // Not on-site
|
||||
var student = StudentBuilder.Default()
|
||||
.WithRanking(flight, 1)
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(student, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Not.Null);
|
||||
Assert.That(warning!.Code, Is.EqualTo("NO_ONSITE_ACTIVITY"));
|
||||
Assert.That(warning.Severity, Is.EqualTo(ValidationSeverity.Error));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region NoIndividualEventRule Tests
|
||||
|
||||
[Test]
|
||||
public void NoIndividualEventRule_StudentHasIndividualEvent_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration
|
||||
{
|
||||
RequireIndividualEvent = true,
|
||||
RequireRegionalEvent = false,
|
||||
RequireOnSiteActivity = false
|
||||
};
|
||||
var rule = new NoIndividualEventRule();
|
||||
|
||||
var flight = EventDefinitionBuilder.Individual("Flight").Build();
|
||||
var student = StudentBuilder.Default()
|
||||
.WithRanking(flight, 1)
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(student, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NoIndividualEventRule_StudentOnlyHasTeamEvents_ReturnsWarning()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration
|
||||
{
|
||||
RequireIndividualEvent = true,
|
||||
NoIndividualEventSeverity = ValidationSeverity.Warning
|
||||
};
|
||||
var rule = new NoIndividualEventRule();
|
||||
|
||||
var robotics = EventDefinitionBuilder.Team("Robotics", 2, 5).Build();
|
||||
var student = StudentBuilder.Default()
|
||||
.WithRanking(robotics, 1)
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(student, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Not.Null);
|
||||
Assert.That(warning!.Code, Is.EqualTo("NO_INDIVIDUAL_EVENT"));
|
||||
Assert.That(warning.Message, Is.EqualTo("No Individual Event"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region TooManyRegionalEventsRule Tests
|
||||
|
||||
[Test]
|
||||
public void TooManyRegionalEventsRule_WithinLimit_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration { MaxRegionalEvents = 3 };
|
||||
var rule = new TooManyRegionalEventsRule();
|
||||
|
||||
var flight = EventDefinitionBuilder.Individual("Flight").AsRegionalEvent().Build();
|
||||
var coding = EventDefinitionBuilder.Individual("Coding").AsRegionalEvent().Build();
|
||||
|
||||
var student = StudentBuilder.Default()
|
||||
.WithRanking(flight, 1)
|
||||
.WithRanking(coding, 2)
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(student, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TooManyRegionalEventsRule_ExceedsLimit_ReturnsWarning()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration
|
||||
{
|
||||
MaxRegionalEvents = 2,
|
||||
TooManyRegionalEventsSeverity = ValidationSeverity.Warning
|
||||
};
|
||||
var rule = new TooManyRegionalEventsRule();
|
||||
|
||||
var flight = EventDefinitionBuilder.Individual("Flight").AsRegionalEvent().Build();
|
||||
var coding = EventDefinitionBuilder.Individual("Coding").AsRegionalEvent().Build();
|
||||
var essays = EventDefinitionBuilder.Individual("Essays").AsRegionalEvent().Build();
|
||||
|
||||
var student = StudentBuilder.Default()
|
||||
.WithRanking(flight, 1)
|
||||
.WithRanking(coding, 2)
|
||||
.WithRanking(essays, 3)
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(student, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Not.Null);
|
||||
Assert.That(warning!.Code, Is.EqualTo("TOO_MANY_REGIONAL_EVENTS"));
|
||||
Assert.That(warning.Message, Does.Contain("3 regional events"));
|
||||
Assert.That(warning.Message, Does.Contain("max recommended: 2"));
|
||||
Assert.That(warning.IconIdentifier, Is.EqualTo("RegionalEvent"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TooManyRegionalEventsRule_ContainsMetadata()
|
||||
{
|
||||
// Arrange
|
||||
var config = new ValidationConfiguration { MaxRegionalEvents = 1 };
|
||||
var rule = new TooManyRegionalEventsRule();
|
||||
|
||||
var flight = EventDefinitionBuilder.Individual("Flight").AsRegionalEvent().Build();
|
||||
var coding = EventDefinitionBuilder.Individual("Coding").AsRegionalEvent().Build();
|
||||
|
||||
var student = StudentBuilder.Default()
|
||||
.WithName("Alice", "Anderson")
|
||||
.WithRanking(flight, 1)
|
||||
.WithRanking(coding, 2)
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var warning = rule.Validate(student, config);
|
||||
|
||||
// Assert
|
||||
Assert.That(warning, Is.Not.Null);
|
||||
Assert.That(warning!.Metadata, Contains.Key("RegionalEventCount"));
|
||||
Assert.That(warning.Metadata, Contains.Key("MaxRegionalEvents"));
|
||||
Assert.That(warning.Metadata["RegionalEventCount"], Is.EqualTo(2));
|
||||
Assert.That(warning.Metadata["MaxRegionalEvents"], Is.EqualTo(1));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
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<Team>[]
|
||||
{
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user