441 lines
13 KiB
C#
441 lines
13 KiB
C#
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
|
|
}
|