using System.IO; using System.Text; using SharepointToolbox.Core.Models; using SharepointToolbox.Services; namespace SharepointToolbox.Tests.Services; public class CsvValidationServiceTests { private readonly CsvValidationService _service = new(); private static Stream ToStream(string content) { return new MemoryStream(Encoding.UTF8.GetBytes(content)); } private static Stream ToStreamWithBom(string content) { var preamble = Encoding.UTF8.GetPreamble(); var bytes = Encoding.UTF8.GetBytes(content); var combined = new byte[preamble.Length + bytes.Length]; preamble.CopyTo(combined, 0); bytes.CopyTo(combined, preamble.Length); return new MemoryStream(combined); } [Fact] public void ParseAndValidateMembers_ValidCsv_ReturnsValidRows() { var csv = "GroupName,GroupUrl,Email,Role\nTeam A,https://site,user@test.com,Member\n"; var rows = _service.ParseAndValidateMembers(ToStream(csv)); Assert.Single(rows); Assert.True(rows[0].IsValid); Assert.Equal("user@test.com", rows[0].Record!.Email); } [Fact] public void ParseAndValidateMembers_InvalidEmail_ReturnsErrors() { var csv = "GroupName,GroupUrl,Email,Role\nTeam A,https://site,not-an-email,Member\n"; var rows = _service.ParseAndValidateMembers(ToStream(csv)); Assert.Single(rows); Assert.False(rows[0].IsValid); Assert.Contains(rows[0].Errors, e => e.Contains("Invalid email")); } [Fact] public void ParseAndValidateMembers_MissingGroup_ReturnsError() { var csv = "GroupName,GroupUrl,Email,Role\n,,user@test.com,Member\n"; var rows = _service.ParseAndValidateMembers(ToStream(csv)); Assert.Single(rows); Assert.False(rows[0].IsValid); Assert.Contains(rows[0].Errors, e => e.Contains("GroupName or GroupUrl")); } [Fact] public void ParseAndValidateSites_TeamWithoutOwner_ReturnsError() { var csv = "Name;Alias;Type;Template;Owners;Members\nSite A;site-a;Team;;;\n"; var rows = _service.ParseAndValidateSites(ToStream(csv)); Assert.Single(rows); Assert.False(rows[0].IsValid); Assert.Contains(rows[0].Errors, e => e.Contains("owner")); } [Fact] public void ParseAndValidateSites_ValidTeam_ReturnsValid() { var csv = "Name;Alias;Type;Template;Owners;Members\nSite A;site-a;Team;;admin@test.com;user@test.com\n"; var rows = _service.ParseAndValidateSites(ToStream(csv)); Assert.Single(rows); Assert.True(rows[0].IsValid); Assert.Equal("Site A", rows[0].Record!.Name); } [Fact] public void ParseAndValidateFolders_ValidCsv_ReturnsValidRows() { var csv = "Level1;Level2;Level3;Level4\nAdmin;HR;;\n"; var rows = _service.ParseAndValidateFolders(ToStream(csv)); Assert.Single(rows); Assert.True(rows[0].IsValid); Assert.Equal("Admin", rows[0].Record!.Level1); Assert.Equal("HR", rows[0].Record!.Level2); } [Fact] public void ParseAndValidateFolders_MissingLevel1_ReturnsError() { var csv = "Level1;Level2;Level3;Level4\n;SubFolder;;\n"; var rows = _service.ParseAndValidateFolders(ToStream(csv)); Assert.Single(rows); Assert.False(rows[0].IsValid); Assert.Contains(rows[0].Errors, e => e.Contains("Level1")); } [Fact] public void ParseAndValidate_BomDetection_WorksWithAndWithoutBom() { var csv = "GroupName,GroupUrl,Email,Role\nTeam A,https://site,user@test.com,Member\n"; var rowsNoBom = _service.ParseAndValidateMembers(ToStream(csv)); var rowsWithBom = _service.ParseAndValidateMembers(ToStreamWithBom(csv)); Assert.Single(rowsNoBom); Assert.Single(rowsWithBom); Assert.True(rowsNoBom[0].IsValid); Assert.True(rowsWithBom[0].IsValid); } [Fact] public void ParseAndValidate_SemicolonDelimiter_DetectedAutomatically() { var csv = "Name;Alias;Type;Template;Owners;Members\nSite A;site-a;Communication;;;;\n"; var rows = _service.ParseAndValidateSites(ToStream(csv)); Assert.Single(rows); Assert.Equal("Site A", rows[0].Record!.Name); Assert.Equal("Communication", rows[0].Record!.Type); } }