45edcf5e5f
This commit removes the obsolete EventAssignment class from Core.Entities and introduces new models in Core.Models, including AssignmentParameters, AssignmentRequirement, PartialTeam, and StudentEventStatistics. The changes enhance the organization of assignment-related data and improve the overall structure of the codebase. Additionally, several files have been updated to include references to the new Core.Models namespace, ensuring consistency across the application.
442 lines
13 KiB
C#
442 lines
13 KiB
C#
using Core.Entities;
|
|
using Core.Models;
|
|
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
|
|
}
|