using EnvelopeRenderer.Desktop.Core.Csv; namespace EnvelopeRenderer.Desktop.Tests; public sealed class CsvPreviewLoaderTests : IDisposable { private readonly string _tempDirectory = Path.Combine(Path.GetTempPath(), "EnvelopeRenderer.Desktop.Tests", Guid.NewGuid().ToString("N")); public CsvPreviewLoaderTests() { Directory.CreateDirectory(_tempDirectory); } public void Dispose() { if (Directory.Exists(_tempDirectory)) { Directory.Delete(_tempDirectory, recursive: true); } } private string WriteCsv(string contents) { var path = Path.Combine(_tempDirectory, $"{Guid.NewGuid():N}.csv"); File.WriteAllText(path, contents); return path; } [Fact] public void TryLoad_ValidHeaderAndRows_ReturnsHeadersAndSampleRows() { var path = WriteCsv( "Full Name,Address1,City\r\n" + "Jane Doe,123 Main St,Springfield\r\n" + "John Smith,456 Oak Ave,Shelbyville\r\n"); var loaded = CsvPreviewLoader.TryLoad(path, out var result, out var errors); Assert.True(loaded); Assert.Empty(errors); Assert.NotNull(result); Assert.Equal(new[] { "Full Name", "Address1", "City" }, result!.Headers); Assert.Equal(2, result.SampleRows.Count); Assert.Equal(new[] { "Jane Doe", "123 Main St", "Springfield" }, result.SampleRows[0]); Assert.Equal(new[] { "John Smith", "456 Oak Ave", "Shelbyville" }, result.SampleRows[1]); } [Fact] public void TryLoad_MoreRowsThanMaxSample_ReturnsOnlyTheBoundedSample() { var lines = new List { "Col1,Col2" }; for (var i = 0; i < 50; i++) { lines.Add($"value{i}a,value{i}b"); } var path = WriteCsv(string.Join("\r\n", lines) + "\r\n"); var loaded = CsvPreviewLoader.TryLoad(path, out var result, out var errors, maxSampleRows: 10); Assert.True(loaded); Assert.Empty(errors); Assert.Equal(10, result!.SampleRows.Count); Assert.Equal(new[] { "value0a", "value0b" }, result.SampleRows[0]); Assert.Equal(new[] { "value9a", "value9b" }, result.SampleRows[9]); } [Fact] public void TryLoad_EmptyFile_ReturnsClearError() { var path = WriteCsv(string.Empty); var loaded = CsvPreviewLoader.TryLoad(path, out var result, out var errors); Assert.False(loaded); Assert.Null(result); Assert.Contains(errors, e => e.Contains("empty", StringComparison.OrdinalIgnoreCase)); } [Fact] public void TryLoad_MalformedCsv_ReturnsClearErrorInsteadOfThrowing() { // An unterminated quoted field is invalid CSV that CsvHelper detects and rejects rather // than silently misparsing. var path = WriteCsv("Full Name,Address1\r\n\"Jane Doe,123 Main St\r\n"); var exception = Record.Exception(() => CsvPreviewLoader.TryLoad(path, out var result, out var errors)); Assert.Null(exception); var loaded = CsvPreviewLoader.TryLoad(path, out var loadedResult, out var loadedErrors); Assert.False(loaded); Assert.Null(loadedResult); Assert.NotEmpty(loadedErrors); } [Fact] public void TryLoad_MissingFile_ReturnsClearError() { var path = Path.Combine(_tempDirectory, "does-not-exist.csv"); var loaded = CsvPreviewLoader.TryLoad(path, out var result, out var errors); Assert.False(loaded); Assert.Null(result); Assert.Contains(errors, e => e.Contains("not found", StringComparison.OrdinalIgnoreCase)); } [Fact] public void TryLoad_BlankPath_ReturnsClearError() { var loaded = CsvPreviewLoader.TryLoad(string.Empty, out var result, out var errors); Assert.False(loaded); Assert.Null(result); Assert.NotEmpty(errors); } [Fact] public void TryLoad_HeaderOnlyFile_ReturnsHeadersWithNoSampleRows() { var path = WriteCsv("Full Name,Address1\r\n"); var loaded = CsvPreviewLoader.TryLoad(path, out var result, out var errors); Assert.True(loaded); Assert.Empty(errors); Assert.Equal(new[] { "Full Name", "Address1" }, result!.Headers); Assert.Empty(result.SampleRows); } }