using System.Text; using Core.Notes; using Core.Parsers; using Tests.Builders; namespace Tests.Parsers; [TestFixture] public class StudentNotesFieldParser_Tests { [SetUp] public void SetUp() { BuilderExtensions.ResetAllBuilders(); } [Test] public void Parse_MatchesStudentAndReportsUnmatched() { var aria = StudentBuilder.Create("Aria", "Chittenden").Build(); const string csv = """ Student Name,Interview Time,Application "Chittenden, Aria",3:20-3:35,x Nobody Here,1:00-1:15,x """; var result = Parse(csv, [aria]); Assert.That(result.IsSuccess, Is.True); Assert.That(result.Matches, Has.Count.EqualTo(1)); Assert.That(result.Matches[0].Student, Is.SameAs(aria)); Assert.That(result.Matches[0].Merge.Changed, Is.True); Assert.That(result.Issues, Has.Count.EqualTo(1)); Assert.That(result.Issues[0].RawStudentName, Is.EqualTo("Nobody Here")); } [Test] public void Parse_SameExistingNote_HasNoChanges() { var aria = StudentBuilder.Create("Aria", "Chittenden").Build(); var existing = ImportedFieldsTable.Merge(null, [ new ImportedField("Interview Time", "3:20-3:35"), new ImportedField("Application", "Yes") ]).Markdown; const string csv = """ Student Name,Interview Time,Application Aria Chittenden,3:20-3:35,x """; var result = Parse(csv, [aria], new Dictionary { [aria.Id] = existing }); Assert.That(result.Matches, Has.Count.EqualTo(1)); Assert.That(result.Matches[0].Merge.Changed, Is.False); Assert.That(result.StudentsWithChanges, Is.EqualTo(0)); } [Test] public void Parse_MissingStudentNameColumn_IsError() { var aria = StudentBuilder.Create("Aria", "Chittenden").Build(); const string csv = """ Name,Interview Time Aria Chittenden,3:20-3:35 """; var result = Parse(csv, [aria]); Assert.That(result.IsSuccess, Is.False); Assert.That(result.Errors, Has.Some.Contains("Student Name")); Assert.That(result.Matches, Is.Empty); } [Test] public void Parse_PreservesFieldsNotInCsv() { var aria = StudentBuilder.Create("Aria", "Chittenden").Build(); var existing = ImportedFieldsTable.Merge(null, [ new ImportedField("Interview Time", "1:00-1:15"), new ImportedField("Teacher Rec 1", "Fuqua") ]).Markdown; const string csv = """ Student Name,Interview Time Aria Chittenden,3:20-3:35 """; var result = Parse(csv, [aria], new Dictionary { [aria.Id] = existing }); var fields = ImportedFieldsTable.ParseFields(result.Matches[0].Merge.Markdown); Assert.That(result.Matches[0].Merge.Changed, Is.True); Assert.That(fields.Single(f => f.Name == "Interview Time").Value, Is.EqualTo("3:20-3:35")); Assert.That(fields.Single(f => f.Name == "Teacher Rec 1").Value, Is.EqualTo("Fuqua")); } [Test] public void Parse_X_BecomesYes_AndBlankFieldsRoundTrip() { var aria = StudentBuilder.Create("Aria", "Chittenden").Build(); const string csv = """ Student Name,Application,Teacher Rec 3 Aria Chittenden,x, """; var first = Parse(csv, [aria]); var markdown = first.Matches[0].Merge.Markdown; Assert.That(ImportedFieldsTable.GetFieldValue(markdown, "Application"), Is.EqualTo("Yes")); Assert.That(ImportedFieldsTable.GetFieldValue(markdown, "Teacher Rec 3"), Is.EqualTo(string.Empty)); var second = Parse(csv, [aria], new Dictionary { [aria.Id] = markdown }); Assert.That(second.Matches[0].Merge.Changed, Is.False); Assert.That(second.StudentsWithChanges, Is.EqualTo(0)); } [Test] public void Parse_DuplicateStudentRows_LastValueWins_OneMatch() { var aria = StudentBuilder.Create("Aria", "Chittenden").Build(); const string csv = """ Student Name,Interview Time,Teacher Rec 1 Aria Chittenden,1:00-1:15,Fuqua "Chittenden, Aria",3:20-3:35,Young """; var result = Parse(csv, [aria]); var fields = ImportedFieldsTable.ParseFields(result.Matches[0].Merge.Markdown); Assert.That(result.Matches, Has.Count.EqualTo(1)); Assert.That(fields.Single(f => f.Name == "Interview Time").Value, Is.EqualTo("3:20-3:35")); Assert.That(fields.Single(f => f.Name == "Teacher Rec 1").Value, Is.EqualTo("Young")); } [Test] public void Parse_NameOnlyCsv_WarnsAndMatchesWithoutFieldChanges() { var aria = StudentBuilder.Create("Aria", "Chittenden").Build(); const string csv = """ Student Name Aria Chittenden """; var result = Parse(csv, [aria]); Assert.That(result.IsSuccess, Is.True); Assert.That(result.Warnings, Has.Some.Contains("No leftover field columns")); Assert.That(result.Matches, Has.Count.EqualTo(1)); Assert.That(result.Matches[0].Merge.Changed, Is.False); } [Test] public void Parse_ExcludesRosterAndRankingColumns() { var aria = StudentBuilder.Create("Aria", "Chittenden").Build(); const string csv = """ Student Name,Grade,TSA year,State ID,Regional ID,National ID,Officer,1,2,TOTAL # OF EVENTS,Interview Time,Application Aria Chittenden,6,1st,,,,,Coding,JSS,2,3:20-3:35,x """; var result = Parse(csv, [aria]); var fields = ImportedFieldsTable.ParseFields(result.Matches[0].Merge.Markdown); Assert.That(result.FieldNames, Is.EquivalentTo(["Interview Time", "Application"])); Assert.That(fields.Select(f => f.Name), Is.EquivalentTo(["Interview Time", "Application"])); Assert.That(fields.Single(f => f.Name == "Application").Value, Is.EqualTo("Yes")); Assert.That(fields.Any(f => f.Name is "Grade" or "1" or "Officer"), Is.False); } [Test] public void Parse_RosterOnlyCsv_HasNoLeftoverFields() { var aria = StudentBuilder.Create("Aria", "Chittenden").Build(); const string csv = """ Student Name,Grade,TSA year,State ID,Regional ID,National ID Aria Chittenden,6,1st,,, """; var result = Parse(csv, [aria]); Assert.That(result.FieldNames, Is.Empty); Assert.That(result.Matches[0].Merge.Changed, Is.False); Assert.That(result.Warnings, Has.Some.Contains("No leftover field columns")); } private static Core.Models.StudentNotesImportResult Parse( string csv, ICollection students, IReadOnlyDictionary? notes = null) { using var reader = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(csv))); using var parser = new StudentNotesFieldParser(reader); return parser.Parse(students, notes ?? new Dictionary()); } }