Refactor event occurrence parsing by introducing modular components for improved maintainability
This commit restructures the EventOccurrenceParser by breaking down its functionality into modular components, including EventDefinitionResolver, LineClassifier, LocationPatternMatcher, SectionHeaderMatcher, TimeLocationParser, and TimeParser. This refactoring enhances code readability and maintainability, allowing for easier updates and testing. Additionally, the TextUtil class has been updated to include input sanitization methods. Comprehensive unit tests have been added to ensure the correctness of the new parsing logic and to validate the handling of various event occurrence scenarios.
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
using Core.Entities;
|
||||
using Core.Parsers.EventOccurrence;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Tests.Parsers.EventOccurrence;
|
||||
|
||||
[TestFixture]
|
||||
public class EventDefinitionResolver_Tests
|
||||
{
|
||||
[Test]
|
||||
public void Resolve_SpecialEventPattern_MeetTheCandidates_ReturnsCorrectDefinition()
|
||||
{
|
||||
// Act
|
||||
var result = EventDefinitionResolver.Resolve("Meet the Candidates Session 1", null);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(EventDefinition.MeetTheCandidates));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Resolve_SpecialEventPattern_ChapterOfficerMeeting_ReturnsCorrectDefinition()
|
||||
{
|
||||
// Act
|
||||
var result = EventDefinitionResolver.Resolve("Chapter Officer Meeting - MS", null);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(EventDefinition.ChapterOfficerMeeting));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Resolve_SpecialEventPattern_VotingDelegateMeeting_ReturnsCorrectDefinition()
|
||||
{
|
||||
// Act
|
||||
var result = EventDefinitionResolver.Resolve("Voting Delegate Meeting", null);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(EventDefinition.VotingDelegateMeeting));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Resolve_SpecialEventPattern_CaseInsensitive_Works()
|
||||
{
|
||||
// Act
|
||||
var result = EventDefinitionResolver.Resolve("MEET THE CANDIDATES", null);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(EventDefinition.MeetTheCandidates));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Resolve_GeneralSchedule_CurrentEventGeneralSchedule_ReturnsGeneralSchedule()
|
||||
{
|
||||
// Act
|
||||
var result = EventDefinitionResolver.Resolve("Some Event Name", EventDefinition.GeneralSchedule);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(EventDefinition.GeneralSchedule));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Resolve_CurrentEventDefinition_ReturnsCurrentEvent()
|
||||
{
|
||||
// Arrange
|
||||
var currentEvent = new EventDefinition { Name = "Test Event" };
|
||||
|
||||
// Act
|
||||
var result = EventDefinitionResolver.Resolve("Some Occurrence", currentEvent);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(currentEvent));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Resolve_SpecialEventPattern_TakesPrecedenceOverCurrentEvent()
|
||||
{
|
||||
// Arrange
|
||||
var currentEvent = new EventDefinition { Name = "Test Event" };
|
||||
|
||||
// Act
|
||||
var result = EventDefinitionResolver.Resolve("Meet the Candidates Session 1", currentEvent);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(EventDefinition.MeetTheCandidates));
|
||||
Assert.That(result, Is.Not.EqualTo(currentEvent));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Resolve_NoMatch_ReturnsNull()
|
||||
{
|
||||
// Act
|
||||
var result = EventDefinitionResolver.Resolve("Unknown Event Name", null);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Resolve_NoMatch_CurrentEventNull_ReturnsNull()
|
||||
{
|
||||
// Act
|
||||
var result = EventDefinitionResolver.Resolve("Unknown Event Name", null);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
using Core.Parsers.EventOccurrence;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Tests.Parsers.EventOccurrence;
|
||||
|
||||
[TestFixture]
|
||||
public class LineClassifier_Tests
|
||||
{
|
||||
[Test]
|
||||
public void IsEmptyLine_EmptyString_ReturnsTrue()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.That(LineClassifier.IsEmptyLine(""), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsEmptyLine_WhitespaceOnly_ReturnsTrue()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.That(LineClassifier.IsEmptyLine(" "), Is.True);
|
||||
Assert.That(LineClassifier.IsEmptyLine("\t\n"), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsEmptyLine_NonEmpty_ReturnsFalse()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.That(LineClassifier.IsEmptyLine("Some text"), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsCommentLine_StartsWithHash_ReturnsTrue()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.That(LineClassifier.IsCommentLine("# This is a comment"), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsCommentLine_NoHash_ReturnsFalse()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.That(LineClassifier.IsCommentLine("Not a comment"), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsContinuationLine_ParentheticalNote_ReturnsTrue()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.That(LineClassifier.IsContinuationLine("(Semifinalists only)"), Is.True);
|
||||
Assert.That(LineClassifier.IsContinuationLine("(Note: Some information)"), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsContinuationLine_StartsWithLowercaseContinuationWord_ReturnsTrue()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.That(LineClassifier.IsContinuationLine("the event will be held"), Is.True);
|
||||
Assert.That(LineClassifier.IsContinuationLine("and participants should arrive"), Is.True);
|
||||
Assert.That(LineClassifier.IsContinuationLine("be sure to register"), Is.True);
|
||||
Assert.That(LineClassifier.IsContinuationLine("or contact the coordinator"), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsContinuationLine_StartsWithLowercase_NonContinuationWord_ReturnsFalse()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.That(LineClassifier.IsContinuationLine("important: bring materials"), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsContinuationLine_StartsWithUppercase_ReturnsFalse()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.That(LineClassifier.IsContinuationLine("Important Event March 15"), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsContinuationLine_ContainsSchedulePosted_ReturnsTrue()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.That(LineClassifier.IsContinuationLine("Schedule Posted on website"), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsContinuationLine_ContainsNote_ReturnsTrue()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.That(LineClassifier.IsContinuationLine("Note: Additional information"), Is.True);
|
||||
Assert.That(LineClassifier.IsContinuationLine("*Note: Important details"), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsContinuationLine_RegularEventLine_ReturnsFalse()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.That(LineClassifier.IsContinuationLine("Test Event March 15 3:00 p.m. Room A"), Is.False);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
using Core.Parsers.EventOccurrence;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Tests.Parsers.EventOccurrence;
|
||||
|
||||
[TestFixture]
|
||||
public class LocationPatternMatcher_Tests
|
||||
{
|
||||
[Test]
|
||||
public void Match_ExactMatch_ReturnsLocation()
|
||||
{
|
||||
// Arrange
|
||||
var patterns = new List<string> { "Room A", "Room B", "Hall C" };
|
||||
|
||||
// Act
|
||||
var result = LocationPatternMatcher.Match("Room A", patterns);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo("Room A"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Match_ExactMatch_CaseInsensitive_ReturnsLocation()
|
||||
{
|
||||
// Arrange
|
||||
var patterns = new List<string> { "Room A" };
|
||||
|
||||
// Act
|
||||
var result = LocationPatternMatcher.Match("room a", patterns);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo("room a"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Match_WildcardPattern_Matches_ReturnsLocation()
|
||||
{
|
||||
// Arrange
|
||||
var patterns = new List<string> { "Room *", "Hall *" };
|
||||
|
||||
// Act
|
||||
var result = LocationPatternMatcher.Match("Room 101", patterns);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo("Room 101"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Match_WildcardPattern_MultipleMatches_ReturnsFirstMatch()
|
||||
{
|
||||
// Arrange
|
||||
var patterns = new List<string> { "Room *", "Exhibit Hall *" };
|
||||
|
||||
// Act
|
||||
var result = LocationPatternMatcher.Match("Room 202", patterns);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo("Room 202"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Match_WildcardPattern_ExhibitHall_Matches()
|
||||
{
|
||||
// Arrange
|
||||
var patterns = new List<string> { "Exhibit Hall *" };
|
||||
|
||||
// Act
|
||||
var result = LocationPatternMatcher.Match("Exhibit Hall C", patterns);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo("Exhibit Hall C"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Match_WildcardPattern_MtgRoom_Matches()
|
||||
{
|
||||
// Arrange
|
||||
var patterns = new List<string> { "Mtg. Room *" };
|
||||
|
||||
// Act
|
||||
var result = LocationPatternMatcher.Match("Mtg. Room 14", patterns);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo("Mtg. Room 14"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Match_NoMatch_ReturnsEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var patterns = new List<string> { "Room *", "Hall *" };
|
||||
|
||||
// Act
|
||||
var result = LocationPatternMatcher.Match("Unknown Location", patterns);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Match_EmptyLocation_ReturnsEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var patterns = new List<string> { "Room *" };
|
||||
|
||||
// Act
|
||||
var result = LocationPatternMatcher.Match("", patterns);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Match_WhitespaceLocation_ReturnsEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var patterns = new List<string> { "Room *" };
|
||||
|
||||
// Act
|
||||
var result = LocationPatternMatcher.Match(" ", patterns);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Match_EmptyPatterns_ReturnsEmpty()
|
||||
{
|
||||
// Arrange
|
||||
var patterns = new List<string>();
|
||||
|
||||
// Act
|
||||
var result = LocationPatternMatcher.Match("Room A", patterns);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Match_PatternWithSpecialCharacters_EscapesCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var patterns = new List<string> { "Room (A)" };
|
||||
|
||||
// Act
|
||||
var result = LocationPatternMatcher.Match("Room (A)", patterns);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo("Room (A)"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Match_LocationWithWhitespace_Trims_ReturnsLocation()
|
||||
{
|
||||
// Arrange
|
||||
var patterns = new List<string> { "Room *" };
|
||||
|
||||
// Act
|
||||
var result = LocationPatternMatcher.Match(" Room 101 ", patterns);
|
||||
|
||||
// Assert
|
||||
// LocationPatternMatcher returns the matched location after normalization (trim)
|
||||
Assert.That(result, Is.EqualTo("Room 101"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
using Core.Entities;
|
||||
using Core.Parsers.EventOccurrence;
|
||||
using FuzzySharp;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Tests.Parsers.EventOccurrence;
|
||||
|
||||
[TestFixture]
|
||||
public class SectionHeaderMatcher_Tests
|
||||
{
|
||||
[Test]
|
||||
public void IsGeneralSchedule_GeneralScheduleLine_ReturnsTrue()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.That(SectionHeaderMatcher.IsGeneralSchedule("General Schedule"), Is.True);
|
||||
Assert.That(SectionHeaderMatcher.IsGeneralSchedule("General Schedule - MS"), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsGeneralSchedule_RegularEvent_ReturnsFalse()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.That(SectionHeaderMatcher.IsGeneralSchedule("Test Event - MS"), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HasSchoolLevel_ContainsMS_ReturnsTrue()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.That(SectionHeaderMatcher.HasSchoolLevel("Test Event - MS"), Is.True);
|
||||
Assert.That(SectionHeaderMatcher.HasSchoolLevel("Test Event MS"), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HasSchoolLevel_ContainsHS_ReturnsTrue()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.That(SectionHeaderMatcher.HasSchoolLevel("Test Event - HS"), Is.True);
|
||||
Assert.That(SectionHeaderMatcher.HasSchoolLevel("Test Event HS"), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HasSchoolLevel_NoSchoolLevel_ReturnsFalse()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.That(SectionHeaderMatcher.HasSchoolLevel("Test Event"), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MatchEventDefinition_ExactMatch_ReturnsEvent()
|
||||
{
|
||||
// Arrange
|
||||
var events = new List<EventDefinition>
|
||||
{
|
||||
new() { Name = "Test Event" },
|
||||
new() { Name = "Another Event" }
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = SectionHeaderMatcher.MatchEventDefinition("Test Event", events);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result!.Name, Is.EqualTo("Test Event"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MatchEventDefinition_CloseMatch_ReturnsEvent()
|
||||
{
|
||||
// Arrange
|
||||
var events = new List<EventDefinition>
|
||||
{
|
||||
new() { Name = "Test Event" },
|
||||
new() { Name = "Another Event" }
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = SectionHeaderMatcher.MatchEventDefinition("Test Evnt", events); // Typo but close
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result!.Name, Is.EqualTo("Test Event"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MatchEventDefinition_NoMatch_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var events = new List<EventDefinition>
|
||||
{
|
||||
new() { Name = "Test Event" }
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = SectionHeaderMatcher.MatchEventDefinition("Completely Different Event", events);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MatchEventDefinition_EmptyEvents_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var events = new List<EventDefinition>();
|
||||
|
||||
// Act
|
||||
var result = SectionHeaderMatcher.MatchEventDefinition("Test Event", events);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MatchEventDefinition_MultipleMatches_ReturnsBestMatch()
|
||||
{
|
||||
// Arrange
|
||||
var events = new List<EventDefinition>
|
||||
{
|
||||
new() { Name = "Test Event" },
|
||||
new() { Name = "Test Event Advanced" },
|
||||
new() { Name = "Another Event" }
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = SectionHeaderMatcher.MatchEventDefinition("Test Event", events);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result!.Name, Is.EqualTo("Test Event")); // Exact match should win
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetBestMatchRatio_ValidEvents_ReturnsRatio()
|
||||
{
|
||||
// Arrange
|
||||
var events = new List<EventDefinition>
|
||||
{
|
||||
new() { Name = "Test Event" }
|
||||
};
|
||||
|
||||
// Act
|
||||
var ratio = SectionHeaderMatcher.GetBestMatchRatio("Test Event", events);
|
||||
|
||||
// Assert
|
||||
Assert.That(ratio, Is.GreaterThan(50)); // Should be high for exact match
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetBestMatchRatio_EmptyEvents_ReturnsZero()
|
||||
{
|
||||
// Arrange
|
||||
var events = new List<EventDefinition>();
|
||||
|
||||
// Act
|
||||
var ratio = SectionHeaderMatcher.GetBestMatchRatio("Test Event", events);
|
||||
|
||||
// Assert
|
||||
Assert.That(ratio, Is.EqualTo(0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
using Core.Models;
|
||||
using Core.Parsers.EventOccurrence;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Tests.Parsers.EventOccurrence;
|
||||
|
||||
[TestFixture]
|
||||
public class TimeLocationParser_Tests
|
||||
{
|
||||
[Test]
|
||||
public void Parse_TimeAndLocation_ExtractsBoth()
|
||||
{
|
||||
// Arrange
|
||||
var locationConfig = new LocationParsingConfiguration
|
||||
{
|
||||
LocationPatterns = new List<string> { "Room *" }
|
||||
};
|
||||
|
||||
// Act
|
||||
TimeLocationParser.Parse("10:30 a.m. Room 101", locationConfig,
|
||||
out string time, out string location, out bool locationParseSuccess);
|
||||
|
||||
// Assert
|
||||
Assert.That(time, Is.EqualTo("10:30 a.m."));
|
||||
Assert.That(location, Is.EqualTo("Room 101"));
|
||||
Assert.That(locationParseSuccess, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_TimeRangeAndLocation_ExtractsTimeRangeAndLocation()
|
||||
{
|
||||
// Arrange
|
||||
var locationConfig = new LocationParsingConfiguration
|
||||
{
|
||||
LocationPatterns = new List<string> { "Room *" }
|
||||
};
|
||||
|
||||
// Act
|
||||
TimeLocationParser.Parse("10:00 a.m. - 12:00 p.m. Room 202", locationConfig,
|
||||
out string time, out string location, out bool locationParseSuccess);
|
||||
|
||||
// Assert
|
||||
Assert.That(time, Is.EqualTo("10:00 a.m. - 12:00 p.m."));
|
||||
Assert.That(location, Is.EqualTo("Room 202"));
|
||||
Assert.That(locationParseSuccess, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_NOONAndLocation_ExtractsBoth()
|
||||
{
|
||||
// Arrange
|
||||
var locationConfig = new LocationParsingConfiguration
|
||||
{
|
||||
LocationPatterns = new List<string> { "Hall *" }
|
||||
};
|
||||
|
||||
// Act
|
||||
TimeLocationParser.Parse("NOON Hall C", locationConfig,
|
||||
out string time, out string location, out bool locationParseSuccess);
|
||||
|
||||
// Assert
|
||||
Assert.That(time, Is.EqualTo("NOON"));
|
||||
Assert.That(location, Is.EqualTo("Hall C"));
|
||||
Assert.That(locationParseSuccess, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_TimeOnly_NoLocation()
|
||||
{
|
||||
// Arrange
|
||||
var locationConfig = new LocationParsingConfiguration
|
||||
{
|
||||
LocationPatterns = new List<string> { "Room *" }
|
||||
};
|
||||
|
||||
// Act
|
||||
TimeLocationParser.Parse("3:00 p.m.", locationConfig,
|
||||
out string time, out string location, out bool locationParseSuccess);
|
||||
|
||||
// Assert
|
||||
Assert.That(time, Is.EqualTo("3:00 p.m."));
|
||||
Assert.That(location, Is.Empty);
|
||||
Assert.That(locationParseSuccess, Is.True); // No location is valid
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_LocationNotMatchingPattern_StillReturnsLocation_ReportsFailure()
|
||||
{
|
||||
// Arrange
|
||||
var locationConfig = new LocationParsingConfiguration
|
||||
{
|
||||
LocationPatterns = new List<string> { "Room *" }
|
||||
};
|
||||
|
||||
// Act
|
||||
TimeLocationParser.Parse("10:00 a.m. Unknown Location", locationConfig,
|
||||
out string time, out string location, out bool locationParseSuccess);
|
||||
|
||||
// Assert
|
||||
Assert.That(time, Is.EqualTo("10:00 a.m."));
|
||||
Assert.That(location, Is.EqualTo("Unknown Location"));
|
||||
Assert.That(locationParseSuccess, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_LocationWithTimeComponent_CleansTimeComponent()
|
||||
{
|
||||
// Arrange
|
||||
var locationConfig = new LocationParsingConfiguration
|
||||
{
|
||||
LocationPatterns = new List<string> { "Exhibit Hall *" }
|
||||
};
|
||||
|
||||
// Act
|
||||
TimeLocationParser.Parse("10:00 a.m. - 12:15 p.m. Exhibit Hall C", locationConfig,
|
||||
out string time, out string location, out bool locationParseSuccess);
|
||||
|
||||
// Assert
|
||||
Assert.That(time, Is.EqualTo("10:00 a.m. - 12:15 p.m."));
|
||||
Assert.That(location, Is.EqualTo("Exhibit Hall C"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_NoLocationConfig_StillExtractsTimeAndLocation()
|
||||
{
|
||||
// Act
|
||||
TimeLocationParser.Parse("3:00 p.m. Room A", null,
|
||||
out string time, out string location, out bool locationParseSuccess);
|
||||
|
||||
// Assert
|
||||
Assert.That(time, Is.EqualTo("3:00 p.m."));
|
||||
Assert.That(location, Is.EqualTo("Room A"));
|
||||
Assert.That(locationParseSuccess, Is.False); // No patterns to match against
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CleanLocationText_RemovesTimeAtStart()
|
||||
{
|
||||
// Act
|
||||
var result = TimeLocationParser.CleanLocationText("- 12:15 p.m. Exhibit Hall C");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo("Exhibit Hall C"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CleanLocationText_RemovesTimeWithoutDash()
|
||||
{
|
||||
// Act
|
||||
var result = TimeLocationParser.CleanLocationText("12:15 p.m. Exhibit Hall C");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo("Exhibit Hall C"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CleanLocationText_RemovesNOONAtStart()
|
||||
{
|
||||
// Act
|
||||
var result = TimeLocationParser.CleanLocationText("- NOON Exhibit Hall C");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo("Exhibit Hall C"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CleanLocationText_OnlyTime_ReturnsEmpty()
|
||||
{
|
||||
// Act
|
||||
var result = TimeLocationParser.CleanLocationText("12:15 p.m.");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CleanLocationText_EmptyString_ReturnsEmpty()
|
||||
{
|
||||
// Act
|
||||
var result = TimeLocationParser.CleanLocationText("");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CleanLocationText_WhitespaceOnly_ReturnsEmpty()
|
||||
{
|
||||
// Act
|
||||
var result = TimeLocationParser.CleanLocationText(" ");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CleanLocationText_NoTimeComponent_ReturnsOriginal()
|
||||
{
|
||||
// Act
|
||||
var result = TimeLocationParser.CleanLocationText("Exhibit Hall C");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo("Exhibit Hall C"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
using Core.Parsers.EventOccurrence;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Tests.Parsers.EventOccurrence;
|
||||
|
||||
[TestFixture]
|
||||
public class TimeParser_Tests
|
||||
{
|
||||
[Test]
|
||||
public void Parse_NOON_Returns12PM()
|
||||
{
|
||||
// Act
|
||||
var result = TimeParser.Parse("NOON");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(new TimeOnly(12, 0, 0)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_TBD_ReturnsMidnight()
|
||||
{
|
||||
// Act
|
||||
var result = TimeParser.Parse("TBD");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(new TimeOnly(0, 0, 0)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_TBD_CaseInsensitive_ReturnsMidnight()
|
||||
{
|
||||
// Act
|
||||
var result = TimeParser.Parse("tbd");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(new TimeOnly(0, 0, 0)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_AMTime_ReturnsCorrectTime()
|
||||
{
|
||||
// Act
|
||||
var result = TimeParser.Parse("10:30 a.m.");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(new TimeOnly(10, 30, 0)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_PMTime_ReturnsCorrectTime()
|
||||
{
|
||||
// Act
|
||||
var result = TimeParser.Parse("3:45 p.m.");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(new TimeOnly(15, 45, 0)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_TimeRange_ExtractsStartTime()
|
||||
{
|
||||
// Act
|
||||
var result = TimeParser.Parse("10:00 a.m. - 12:00 p.m.");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(new TimeOnly(10, 0, 0)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_TimeRangeWithNOON_ExtractsStartTime()
|
||||
{
|
||||
// Act
|
||||
var result = TimeParser.Parse("10:30 a.m. - NOON");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(new TimeOnly(10, 30, 0)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_TimeWithoutMinutes_ReturnsCorrectTime()
|
||||
{
|
||||
// Act
|
||||
var result = TimeParser.Parse("3 p.m.");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(new TimeOnly(15, 0, 0)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_TimeWithoutColon_ReturnsCorrectTime()
|
||||
{
|
||||
// Act
|
||||
var result = TimeParser.Parse("1030 a.m.");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(new TimeOnly(10, 30, 0)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_12PM_Returns12PM_NotMidnight()
|
||||
{
|
||||
// Act
|
||||
var result = TimeParser.Parse("12:00 p.m.");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(new TimeOnly(12, 0, 0)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_12AM_ReturnsMidnight()
|
||||
{
|
||||
// Act
|
||||
var result = TimeParser.Parse("12:00 a.m.");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(new TimeOnly(0, 0, 0)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parse_InvalidFormat_ThrowsFormatException()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.Throws<FormatException>(() => TimeParser.Parse("invalid time format"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExtractStartTime_Range_ReturnsStartTime()
|
||||
{
|
||||
// Act
|
||||
var result = TimeParser.ExtractStartTime("10:00 a.m. - 12:00 p.m.");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo("10:00 a.m."));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExtractStartTime_NoRange_ReturnsOriginal()
|
||||
{
|
||||
// Act
|
||||
var result = TimeParser.ExtractStartTime("3:00 p.m.");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo("3:00 p.m."));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExtractStartTime_RangeWithNOON_ReturnsStartTime()
|
||||
{
|
||||
// Act
|
||||
var result = TimeParser.ExtractStartTime("10:30 a.m. - NOON");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo("10:30 a.m."));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
using Core.Utility;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Tests.Utility;
|
||||
|
||||
[TestFixture]
|
||||
public class TextUtil_Tests
|
||||
{
|
||||
[Test]
|
||||
public void ParseDate_ValidInput_ReturnsCorrectDate()
|
||||
{
|
||||
// Arrange & Act
|
||||
var result = TextUtil.ParseDate("January", "15", 2025);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(new DateOnly(2025, 1, 15)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseDate_AllMonths_AreSupported()
|
||||
{
|
||||
// Arrange
|
||||
var months = new[] { "January", "February", "March", "April", "May", "June",
|
||||
"July", "August", "September", "October", "November", "December" };
|
||||
var expectedDates = new[]
|
||||
{
|
||||
new DateOnly(2025, 1, 15),
|
||||
new DateOnly(2025, 2, 15),
|
||||
new DateOnly(2025, 3, 15),
|
||||
new DateOnly(2025, 4, 15),
|
||||
new DateOnly(2025, 5, 15),
|
||||
new DateOnly(2025, 6, 15),
|
||||
new DateOnly(2025, 7, 15),
|
||||
new DateOnly(2025, 8, 15),
|
||||
new DateOnly(2025, 9, 15),
|
||||
new DateOnly(2025, 10, 15),
|
||||
new DateOnly(2025, 11, 15),
|
||||
new DateOnly(2025, 12, 15),
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
for (int i = 0; i < months.Length; i++)
|
||||
{
|
||||
var result = TextUtil.ParseDate(months[i], "15", 2025);
|
||||
Assert.That(result, Is.EqualTo(expectedDates[i]),
|
||||
$"Month {months[i]} should parse correctly");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseDate_CaseInsensitive_Works()
|
||||
{
|
||||
// Arrange & Act
|
||||
var result1 = TextUtil.ParseDate("JANUARY", "15", 2025);
|
||||
var result2 = TextUtil.ParseDate("january", "15", 2025);
|
||||
var result3 = TextUtil.ParseDate("JaNuArY", "15", 2025);
|
||||
|
||||
// Assert
|
||||
Assert.That(result1, Is.EqualTo(new DateOnly(2025, 1, 15)));
|
||||
Assert.That(result2, Is.EqualTo(new DateOnly(2025, 1, 15)));
|
||||
Assert.That(result3, Is.EqualTo(new DateOnly(2025, 1, 15)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseDate_InvalidMonth_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange, Act & Assert
|
||||
Assert.Throws<ArgumentException>(() => TextUtil.ParseDate("InvalidMonth", "15", 2025));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseDate_InvalidDay_ThrowsFormatException()
|
||||
{
|
||||
// Arrange, Act & Assert
|
||||
Assert.Throws<FormatException>(() => TextUtil.ParseDate("January", "abc", 2025));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseDate_InvalidDate_ThrowsArgumentOutOfRangeException()
|
||||
{
|
||||
// Arrange, Act & Assert - February 30 doesn't exist
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => TextUtil.ParseDate("February", "30", 2025));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseDate_LeapYear_February29_Works()
|
||||
{
|
||||
// Arrange & Act
|
||||
var result = TextUtil.ParseDate("February", "29", 2024); // 2024 is a leap year
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(new DateOnly(2024, 2, 29)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseDate_NonLeapYear_February29_Throws()
|
||||
{
|
||||
// Arrange, Act & Assert - 2025 is not a leap year
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => TextUtil.ParseDate("February", "29", 2025));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseDate_SingleDigitDay_Works()
|
||||
{
|
||||
// Arrange & Act
|
||||
var result = TextUtil.ParseDate("March", "3", 2025);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(new DateOnly(2025, 3, 3)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseDate_DifferentYears_Works()
|
||||
{
|
||||
// Arrange & Act
|
||||
var result2024 = TextUtil.ParseDate("January", "1", 2024);
|
||||
var result2025 = TextUtil.ParseDate("January", "1", 2025);
|
||||
var result2026 = TextUtil.ParseDate("January", "1", 2026);
|
||||
|
||||
// Assert
|
||||
Assert.That(result2024, Is.EqualTo(new DateOnly(2024, 1, 1)));
|
||||
Assert.That(result2025, Is.EqualTo(new DateOnly(2025, 1, 1)));
|
||||
Assert.That(result2026, Is.EqualTo(new DateOnly(2026, 1, 1)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseDate_FirstDayOfMonth_Works()
|
||||
{
|
||||
// Arrange & Act
|
||||
var result = TextUtil.ParseDate("December", "1", 2025);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(new DateOnly(2025, 12, 1)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseDate_LastDayOfMonth_Works()
|
||||
{
|
||||
// Arrange & Act
|
||||
var result = TextUtil.ParseDate("January", "31", 2025);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.EqualTo(new DateOnly(2025, 1, 31)));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user