From 4345679a9e37f14a63e4b3cf830ad928dff75f10 Mon Sep 17 00:00:00 2001 From: Travis Frisinger Date: Thu, 29 May 2025 13:35:39 -0600 Subject: [PATCH] Fix bugs and add async methods with tests This commit: 1. Adds proper error handling in MoveWithOverwrite and Rename methods 2. Adds consistent null checking across all methods 3. Updates stream disposal to use modern C# syntax 4. Adds file name validation 5. Adds comprehensive XML documentation comments 6. Adds asynchronous versions of all synchronous methods 7. Fixes the Append test that was incorrectly using Write 8. Adds comprehensive tests for all new async methods --- .../FileSystemTests.cs | 1874 +++++++++++------ source/StoneAge.Data.FileSystem/Document.cs | 54 +- .../Domain/IDocument.cs | 28 +- .../Domain/IFileSystem.cs | 164 +- source/StoneAge.Data.FileSystem/FileSystem.cs | 688 +++--- 5 files changed, 1827 insertions(+), 981 deletions(-) diff --git a/source/StoneAge.Data.FileSystem.Tests/FileSystemTests.cs b/source/StoneAge.Data.FileSystem.Tests/FileSystemTests.cs index 77d96a4..9dddd38 100644 --- a/source/StoneAge.Data.FileSystem.Tests/FileSystemTests.cs +++ b/source/StoneAge.Data.FileSystem.Tests/FileSystemTests.cs @@ -1,694 +1,1180 @@ -using System; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using FluentAssertions; -using NUnit.Framework; -using StoneAge.FileStore.Domain; - -namespace StoneAge.FileStore.Tests -{ - [TestFixture] - public class FileSystemTests - { - [TestFixture] - class Write - { - [Test] - public async Task WhenFileAndPathValid_ExpectFileWritten() - { - //---------------Arrange------------------- - var path = Path.GetTempPath(); - var fileName = Guid.NewGuid()+".csv"; - var document = Create_CsvFile(fileName); - - var sut = new FileSystem(); - //---------------Act---------------------- - var result = await sut.Write(path, document); - //---------------Assert----------------------- - var fileWritten = File.Exists(result.FullFilePath); - result.HadError.Should().BeFalse(); - fileWritten.Should().BeTrue(); - } - - [Test] - public async Task WhenFileAndPathContainNewSubDirectories_ExpectFileWritten() - { - //---------------Arrange------------------- - var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()); - var fileName = Guid.NewGuid() + ".csv"; - var document = Create_CsvFile(fileName); - - var sut = new FileSystem(); - //---------------Act---------------------- - var result = await sut.Write(path, document); - //---------------Assert----------------------- - var fileWritten = File.Exists(Path.Combine(path, fileName)); - result.HadError.Should().BeFalse(); - fileWritten.Should().BeTrue(); - } - - [TestCase(" ")] - [TestCase("")] - [TestCase(null)] - public async Task WhenPathContainsNullOrWhiteSpace_ExpectErrorMessage(string path) - { - //---------------Arrange------------------- - var document = Create_CsvFile("test.csv"); - - var sut = new FileSystem(); - //---------------Act---------------------- - var result = await sut.Write(path, document); - //---------------Assert----------------------- - result.HadError.Should().BeTrue(); - } - - [TestCase("abc")] - [TestCase("~f0")] - public async Task WhenRelativePath_ExpectFileWritten(string path) - { - //---------------Arrange------------------- - var document = Create_CsvFile("test.csv"); - - var sut = new FileSystem(); - //---------------Act---------------------- - var result = await sut.Write(path, document); - //---------------Assert----------------------- - var fileWritten = File.Exists(Path.Combine(path, "test.csv")); - result.HadError.Should().BeFalse(); - fileWritten.Should().BeTrue(); - } - - [Test] - public async Task WhenFileAndPathValid_ExpectFullFilePathReturned() - { - //---------------Arrange------------------- - var path = Path.GetTempPath(); - var fileName = Guid.NewGuid() + ".csv"; - var document = Create_CsvFile(fileName); - - var sut = new FileSystem(); - //---------------Act---------------------- - var result = await sut.Write(path, document); - //---------------Assert----------------------- - var expected = Path.Combine(path, fileName); - result.FullFilePath.Should().Be(expected); - } - - [Test] - public async Task WhenFileExist_ExpectItOverWritten() - { - //---------------Arrange------------------- - var path = Path.GetTempPath(); - var fileName = Guid.NewGuid() + ".csv"; - Write_File_Contents_For_Testing(path, fileName); - var document = Create_CsvFile(fileName); - - var sut = new FileSystem(); - //---------------Act---------------------- - var result = await sut.Write(path, document); - //---------------Assert----------------------- - var contents = File.ReadAllBytes(result.FullFilePath); - contents.Should().BeEquivalentTo(new byte[5]); - } - - [Test] - public async Task WhenFileDataIsNull_ExpectErrorMessage() - { - //---------------Arrange------------------- - var path = Path.GetTempPath(); - var fileName = Guid.NewGuid() + ".txt"; - var document = new DocumentBuilder() - .With_Name(fileName) - .With_Bytes(null) // explicitly set null data - .Create_Document(); - - var sut = new FileSystem(); - //---------------Act---------------------- - var result = await sut.Write(path, document); - //---------------Assert----------------------- - result.HadError.Should().BeTrue(); - result.ErrorMessages.Should().Contain("No file data provided; cannot write file."); - } - - private static void Write_File_Contents_For_Testing(string path, string fileName) - { - File.WriteAllText(Path.Combine(path, fileName), "test line"); - } - } - - [TestFixture] - class Append - { - [Test] - public async Task WhenFileAndPathValid_ExpectFileWritten() - { - //---------------Arrange------------------- - var path = Path.GetTempPath(); - var fileName = Guid.NewGuid() + ".csv"; - var document = Create_CsvFile(fileName); - - var sut = new FileSystem(); - //---------------Act---------------------- - var result = await sut.Append(path, document); - //---------------Assert----------------------- - var fileWritten = File.Exists(result.FullFilePath); - result.HadError.Should().BeFalse(); - fileWritten.Should().BeTrue(); - } - - [Test] - public async Task WhenFileAndPathContainNewSubDirectories_ExpectFileWritten() - { - //---------------Arrange------------------- - var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()); - var fileName = Guid.NewGuid() + ".csv"; - var document = Create_CsvFile(fileName); - - var sut = new FileSystem(); - //---------------Act---------------------- - var result = await sut.Write(path, document); - //---------------Assert----------------------- - var fileWritten = File.Exists(Path.Combine(path, fileName)); - result.HadError.Should().BeFalse(); - fileWritten.Should().BeTrue(); - } - - [TestCase(" ")] - [TestCase("")] - [TestCase(null)] - public async Task WhenPathContainsNullOrWhiteSpace_ExpectErrorMessage(string path) - { - //---------------Arrange------------------- - var document = Create_CsvFile("test.csv"); - - var sut = new FileSystem(); - //---------------Act---------------------- - var result = await sut.Write(path, document); - //---------------Assert----------------------- - result.HadError.Should().BeTrue(); - } - - [TestCase("abc")] - [TestCase("~f0")] - public async Task WhenRelativePath_ExpectFileWritten(string path) - { - //---------------Arrange------------------- - var document = Create_CsvFile("test.csv"); - - var sut = new FileSystem(); - //---------------Act---------------------- - var result = await sut.Write(path, document); - //---------------Assert----------------------- - var fileWritten = File.Exists(Path.Combine(path, "test.csv")); - result.HadError.Should().BeFalse(); - fileWritten.Should().BeTrue(); - } - - [Test] - public async Task WhenFileAndPathValid_ExpectFullFilePathReturned() - { - //---------------Arrange------------------- - var path = Path.GetTempPath(); - var fileName = Guid.NewGuid() + ".csv"; - var document = Create_CsvFile(fileName); - - var sut = new FileSystem(); - //---------------Act---------------------- - var result = await sut.Write(path, document); - //---------------Assert----------------------- - var expected = Path.Combine(path, fileName); - result.FullFilePath.Should().Be(expected); - } - } - - [TestFixture] - class List - { - [Test] - public void WhenDirectoryExist_ExpectContents() - { - //---------------Arrange------------------- - var path = Path.GetTempPath(); - - var sut = new FileSystem(); - //---------------Act---------------------- - var result = sut.List(path); - //---------------Assert----------------------- - result.Count().Should().BeGreaterThanOrEqualTo(1); - } - - [Test] - public void WhenFilePassedIn_ExpectEmptyList() - { - //---------------Arrange------------------- - var path = Path.GetTempFileName(); - - var sut = new FileSystem(); - //---------------Act---------------------- - var result = sut.List(path); - //---------------Assert----------------------- - result.Should().BeEmpty(); - } - - [TestCase(" ")] - [TestCase("")] - [TestCase(null)] - public void WhenNullOrWhiteSpaceDirectory_ExpectEmptyList(string path) - { - //---------------Arrange------------------- - var sut = new FileSystem(); - //---------------Act---------------------- - var result = sut.List(path); - //---------------Assert----------------------- - result.Should().BeEmpty(); - } - } - - [TestFixture] - class Exists - { - [Test] - public void WhenFileDoesNotExist_ExpectFalse() - { - //---------------Arrange------------------- - var path = Create_Missing_File(); - - var sut = new FileSystem(); - //---------------Act---------------------- - var result = sut.Exists(path); - //---------------Assert----------------------- - result.Should().BeFalse(); - } - - [Test] - public void WhenFileExist_ExpectTrue() - { - //---------------Arrange------------------- - var path = Create_File(); - - var sut = new FileSystem(); - //---------------Act---------------------- - var result = sut.Exists(path); - //---------------Assert----------------------- - result.Should().BeTrue(); - } - - [TestCase(" ")] - [TestCase("")] - [TestCase(null)] - public void WhenFileNullOrWhiteSpace_ExpectTrue(string path) - { - //---------------Arrange------------------- - var sut = new FileSystem(); - //---------------Act---------------------- - var result = sut.Exists(path); - //---------------Assert----------------------- - result.Should().BeFalse(); - } - - [Test] - public void WhenDirectoryExist_ExpectTrue() - { - //---------------Arrange------------------- - var path = Path.GetTempPath(); - - var sut = new FileSystem(); - //---------------Act---------------------- - var result = sut.Exists(path); - //---------------Assert----------------------- - result.Should().BeTrue(); - } - } - - [TestFixture] - class Delete - { - [Test] - public void WhenFileExist_ExpectItIsRemoved() - { - //---------------Arrange------------------- - var path = Create_File(); - - var sut = new FileSystem(); - //---------------Act---------------------- - sut.Delete(path); - //---------------Assert----------------------- - var fileExists = File.Exists(path); - fileExists.Should().BeFalse(); - } - - [Test] - public void WhenFileDoesNotExist_ExpectNothingToHappen() - { - //---------------Arrange------------------- - var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - - var sut = new FileSystem(); - //---------------Act---------------------- - sut.Delete(path); - //---------------Assert----------------------- - var fileExists = File.Exists(path); - fileExists.Should().BeFalse(); - } - - [TestCase(" ")] - [TestCase("")] - [TestCase(null)] - public void WhenFileNullOrWhitespace_ExpectNoExceptionsThrown(string path) - { - //---------------Arrange------------------- - var sut = new FileSystem(); - //---------------Act---------------------- - Assert.DoesNotThrow(()=>sut.Delete(path)); - //---------------Assert----------------------- - var fileExists = File.Exists(path); - fileExists.Should().BeFalse(); - } - - [Test] - public void WhenDirectoryExist_ExpectItIsRemoved() - { - //---------------Arrange------------------- - var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - Directory.CreateDirectory(path); - - var sut = new FileSystem(); - //---------------Act---------------------- - sut.Delete(path); - //---------------Assert----------------------- - var fileExists = File.Exists(path); - fileExists.Should().BeFalse(); - } - } - - [TestFixture] - class ReadDocument - { - [Test] - public void GivenFileExist_ExpectDocumentWithBytesReturned() - { - //---------------Arrange------------------- - var contents = "hi, this is some text for a file"; - - var path = Create_File(contents); - - var sut = new FileSystem(); - //---------------Act---------------------- - var actual = sut.GetDocument(path); - //---------------Assert----------------------- - var expected = "hi, this is some text for a file"; - actual.ToString().Should().Be(expected); - } - - [Test] - public void GivenFileDoesExist_ExpectNullDocument() - { - //---------------Arrange------------------- - var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - - var sut = new FileSystem(); - //---------------Act---------------------- - var actual = sut.GetDocument(path); - //---------------Assert----------------------- - actual.Should().Be(FileSystem.NullDocument); - } - - [Test] - public void GivenNullPath_ExpectNullDocument() - { - //---------------Arrange------------------- - var sut = new FileSystem(); - //---------------Act---------------------- - var actual = sut.GetDocument(null); - //---------------Assert----------------------- - actual.Should().Be(FileSystem.NullDocument); - } - } - - [TestFixture] - class Read - { - [Test] - public void GivenFileExist_ExpectDocumentWithBytesReturned() - { - //---------------Arrange------------------- - var contents = "hi, this is some text for a file"; - - var path = Create_File(contents); - - var sut = new FileSystem(); - //---------------Act---------------------- - var actual = sut.Read(path); - //---------------Assert----------------------- - var expected = "hi, this is some text for a file"; - actual.ToString().Should().Be(expected); - } - - [Test] - public void GivenFileDoesExist_ExpectNullDocument() - { - //---------------Arrange------------------- - var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - - var sut = new FileSystem(); - //---------------Act---------------------- - var actual = sut.Read(path); - //---------------Assert----------------------- - actual.Should().Be(FileSystem.NullDocument); - } - - [Test] - public void GivenNullPath_ExpectNullDocument() - { - //---------------Arrange------------------- - var sut = new FileSystem(); - //---------------Act---------------------- - var actual = sut.Read(null); - //---------------Assert----------------------- - actual.Should().Be(FileSystem.NullDocument); - } - } - - [TestFixture] - class ReadAllLines - { - [Test] - public async Task GivenFileExist_ExpectDocumentWithBytesReturned() - { - //---------------Arrange------------------- - var path = Create_File_With_Million_Lines(); - - var sut = new FileSystem(); - //---------------Act---------------------- - var actual = await sut.ReadAllLines(path); - //---------------Assert----------------------- - var expectedFirst = "GlobalRank,TldRank,Domain,TLD,RefSubNets,RefIPs,IDN_Domain,IDN_TLD,PrevGlobalRank,PrevTldRank,PrevRefSubNets,PrevRefIPs"; - var expectedLast = "1000000,499336,alexandrevicenzi.com,com,341,364,alexandrevicenzi.com,com,982364,490355,345,368"; - var enumerable = actual as string[] ?? actual.ToArray(); - enumerable.Count().Should().Be(1000001); - enumerable.FirstOrDefault().Should().BeEquivalentTo(expectedFirst); - enumerable.LastOrDefault().Should().BeEquivalentTo(expectedLast); - } - - [Test] - public void GivenFileDoesExist_ExpectException() - { - //---------------Arrange------------------- - var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - - var sut = new FileSystem(); - //---------------Act---------------------- - var actual = Assert.ThrowsAsync(async ()=>await sut.ReadAllLines(path)); - //---------------Assert----------------------- - actual?.Message.Should().NotBeEmpty(); - } - - [Test] - public void GivenNullPath_ExpectException() - { - //---------------Arrange------------------- - var sut = new FileSystem(); - //---------------Act---------------------- - var actual = Assert.ThrowsAsync(async () => await sut.ReadAllLines(null)); - //---------------Assert----------------------- - actual?.Message.Should().NotBeEmpty(); - } - } - - [TestFixture] - class Move - { - [Test] - public void GivenFileExist_ExpectItIsMoved() - { - //---------------Arrange------------------- - var file = Create_File(); - var newFileName = Create_File(); - File.Delete(newFileName); - - var sut = new FileSystem(); - //---------------Act---------------------- - var actual = sut.Move(file, newFileName); - //---------------Assert----------------------- - var oldFileExist = File.Exists(file); - var newFileExist = File.Exists(newFileName); - - actual.Should().BeTrue(); - oldFileExist.Should().BeFalse(); - newFileExist.Should().BeTrue(); - } - - [Test] - public void GivenFileDoesNotExist_ExpectFalse() - { - //---------------Arrange------------------- - var file = Create_File(); - var newFileName = Create_File(); - File.Delete(newFileName); - File.Delete(file); - - var sut = new FileSystem(); - //---------------Act---------------------- - var actual = sut.Move(file, newFileName); - //---------------Assert----------------------- - var oldFileExist = File.Exists(file); - var newFileExist = File.Exists(newFileName); - - actual.Should().BeFalse(); - oldFileExist.Should().BeFalse(); - newFileExist.Should().BeFalse(); - } - - [Test] - public void GivenDestinationFileExist_ExpectItIsNotMoved() - { - //---------------Arrange------------------- - var file = Create_File(); - var newFileName = Create_File(); - - var sut = new FileSystem(); - //---------------Act---------------------- - var actual = sut.Move(file, newFileName); - //---------------Assert----------------------- - var oldFileExist = File.Exists(file); - var newFileExist = File.Exists(newFileName); - - actual.Should().BeFalse(); - oldFileExist.Should().BeTrue(); - newFileExist.Should().BeTrue(); - } - } - - [TestFixture] - class MoveWithOverwrite - { - [Test] - public void GivenDestinationFileExist_ExpectItIsMoved() - { - //---------------Arrange------------------- - var file = Create_File(); - var newFileName = Create_File(); - - var sut = new FileSystem(); - //---------------Act---------------------- - var actual = sut.MoveWithOverwrite(file, newFileName); - //---------------Assert----------------------- - var oldFileExist = File.Exists(file); - var newFileExist = File.Exists(newFileName); - - actual.Should().BeTrue(); - oldFileExist.Should().BeFalse(); - newFileExist.Should().BeTrue(); - } - } - - [TestFixture] - class Rename - { - [Test] - public void GivenFileExist_ExpectItIsRenamed() - { - //---------------Arrange------------------- - var file = Create_File(); - var newFileName = $"{Guid.NewGuid()}-moved.txt"; - - var sut = new FileSystem(); - //---------------Act---------------------- - var actual = sut.Rename(file, newFileName); - //---------------Assert----------------------- - var oldFileExist = File.Exists(file); - var newFileExist = File.Exists(Path.Combine(Path.GetTempPath(), newFileName)); - - actual.Should().BeTrue(); - oldFileExist.Should().BeFalse(); - newFileExist.Should().BeTrue(); - } - - [Test] - public void GivenFileDoesNotExist_ExpectFalse() - { - //---------------Arrange------------------- - var file = Create_File(); - var newFileName = $"{Guid.NewGuid()}-moved.txt"; - File.Delete(file); - - var sut = new FileSystem(); - //---------------Act---------------------- - var actual = sut.Rename(file, newFileName); - //---------------Assert----------------------- - var oldFileExist = File.Exists(file); - var newFileExist = File.Exists(Path.Combine(Path.GetTempPath(), newFileName)); - - actual.Should().BeFalse(); - oldFileExist.Should().BeFalse(); - newFileExist.Should().BeFalse(); - } - } - - private static string Create_File_With_Million_Lines() - { - var tmp = Path.GetTempPath(); - var path = Path.Combine(tmp, Guid.NewGuid().ToString()); - - var location = TestContext.CurrentContext.WorkDirectory; - var moveFilePath = Path.Combine(location, "majestic_million.csv"); - File.Move(moveFilePath, path); - - return path; - } - - private static string Create_File(string content) - { - var tmp = Path.GetTempPath(); - var path = Path.Combine(tmp, Guid.NewGuid().ToString()); - - File.WriteAllText(path, content); - - return path; - } - - private static string Create_File() - { - return Create_File(string.Empty); - } - - private static string Create_Missing_File() - { - var tmp = Path.GetTempPath(); - var path = Path.Combine(tmp, Guid.NewGuid().ToString()); - - return path; - } - - private static IDocument Create_CsvFile(string fileName) - { - var csvFile = new DocumentBuilder() - .With_Name(fileName) - .With_Bytes(new byte[5]) - .Create_Document(); - return csvFile; - } - - } -} +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using FluentAssertions; +using NUnit.Framework; +using StoneAge.FileStore.Domain; + +namespace StoneAge.FileStore.Tests +{ + [TestFixture] + public class FileSystemTests + { + [TestFixture] + class Write + { + [Test] + public async Task WhenFileAndPathValid_ExpectFileWritten() + { + //---------------Arrange------------------- + var path = Path.GetTempPath(); + var fileName = Guid.NewGuid()+".csv"; + var document = Create_CsvFile(fileName); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = await sut.Write(path, document); + //---------------Assert----------------------- + var fileWritten = File.Exists(result.FullFilePath); + result.HadError.Should().BeFalse(); + fileWritten.Should().BeTrue(); + } + + [Test] + public async Task WhenFileAndPathContainNewSubDirectories_ExpectFileWritten() + { + //---------------Arrange------------------- + var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()); + var fileName = Guid.NewGuid() + ".csv"; + var document = Create_CsvFile(fileName); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = await sut.Write(path, document); + //---------------Assert----------------------- + var fileWritten = File.Exists(Path.Combine(path, fileName)); + result.HadError.Should().BeFalse(); + fileWritten.Should().BeTrue(); + } + + [TestCase(" ")] + [TestCase("")] + [TestCase(null)] + public async Task WhenPathContainsNullOrWhiteSpace_ExpectErrorMessage(string path) + { + //---------------Arrange------------------- + var document = Create_CsvFile("test.csv"); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = await sut.Write(path, document); + //---------------Assert----------------------- + result.HadError.Should().BeTrue(); + } + + [TestCase("abc")] + [TestCase("~f0")] + public async Task WhenRelativePath_ExpectFileWritten(string path) + { + //---------------Arrange------------------- + var document = Create_CsvFile("test.csv"); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = await sut.Write(path, document); + //---------------Assert----------------------- + var fileWritten = File.Exists(Path.Combine(path, "test.csv")); + result.HadError.Should().BeFalse(); + fileWritten.Should().BeTrue(); + } + + [Test] + public async Task WhenFileAndPathValid_ExpectFullFilePathReturned() + { + //---------------Arrange------------------- + var path = Path.GetTempPath(); + var fileName = Guid.NewGuid() + ".csv"; + var document = Create_CsvFile(fileName); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = await sut.Write(path, document); + //---------------Assert----------------------- + var expected = Path.Combine(path, fileName); + result.FullFilePath.Should().Be(expected); + } + + [Test] + public async Task WhenFileExist_ExpectItOverWritten() + { + //---------------Arrange------------------- + var path = Path.GetTempPath(); + var fileName = Guid.NewGuid() + ".csv"; + Write_File_Contents_For_Testing(path, fileName); + var document = Create_CsvFile(fileName); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = await sut.Write(path, document); + //---------------Assert----------------------- + var contents = File.ReadAllBytes(result.FullFilePath); + contents.Should().BeEquivalentTo(new byte[5]); + } + + [Test] + public async Task WhenFileDataIsNull_ExpectErrorMessage() + { + //---------------Arrange------------------- + var path = Path.GetTempPath(); + var fileName = Guid.NewGuid() + ".txt"; + var document = new DocumentBuilder() + .With_Name(fileName) + .With_Bytes(null) // explicitly set null data + .Create_Document(); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = await sut.Write(path, document); + //---------------Assert----------------------- + result.HadError.Should().BeTrue(); + result.ErrorMessages.Should().Contain("No file data provided; cannot write file."); + } + + private static void Write_File_Contents_For_Testing(string path, string fileName) + { + File.WriteAllText(Path.Combine(path, fileName), "test line"); + } + } + + [TestFixture] + class Append + { + [Test] + public async Task WhenFileAndPathValid_ExpectFileWritten() + { + //---------------Arrange------------------- + var path = Path.GetTempPath(); + var fileName = Guid.NewGuid() + ".csv"; + var document = Create_CsvFile(fileName); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = await sut.Append(path, document); + //---------------Assert----------------------- + var fileWritten = File.Exists(result.FullFilePath); + result.HadError.Should().BeFalse(); + fileWritten.Should().BeTrue(); + } + + [Test] + public async Task WhenFileAndPathContainNewSubDirectories_ExpectFileWritten() + { + //---------------Arrange------------------- + var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()); + var fileName = Guid.NewGuid() + ".csv"; + var document = Create_CsvFile(fileName); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = await sut.Append(path, document); + //---------------Assert----------------------- + var fileWritten = File.Exists(Path.Combine(path, fileName)); + result.HadError.Should().BeFalse(); + fileWritten.Should().BeTrue(); + } + + [TestCase(" ")] + [TestCase("")] + [TestCase(null)] + public async Task WhenPathContainsNullOrWhiteSpace_ExpectErrorMessage(string path) + { + //---------------Arrange------------------- + var document = Create_CsvFile("test.csv"); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = await sut.Append(path, document); + //---------------Assert----------------------- + result.HadError.Should().BeTrue(); + } + + [TestCase("abc")] + [TestCase("~f0")] + public async Task WhenRelativePath_ExpectFileWritten(string path) + { + //---------------Arrange------------------- + var document = Create_CsvFile("test.csv"); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = await sut.Append(path, document); + //---------------Assert----------------------- + var fileWritten = File.Exists(Path.Combine(path, "test.csv")); + result.HadError.Should().BeFalse(); + fileWritten.Should().BeTrue(); + } + + [Test] + public async Task WhenFileAndPathValid_ExpectFullFilePathReturned() + { + //---------------Arrange------------------- + var path = Path.GetTempPath(); + var fileName = Guid.NewGuid() + ".csv"; + var document = Create_CsvFile(fileName); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = await sut.Append(path, document); + //---------------Assert----------------------- + var expected = Path.Combine(path, fileName); + result.FullFilePath.Should().Be(expected); + } + } + + [TestFixture] + class List + { + [Test] + public void WhenDirectoryExist_ExpectContents() + { + //---------------Arrange------------------- + var path = Path.GetTempPath(); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = sut.List(path); + //---------------Assert----------------------- + result.Count().Should().BeGreaterThanOrEqualTo(1); + } + + [Test] + public void WhenFilePassedIn_ExpectEmptyList() + { + //---------------Arrange------------------- + var path = Path.GetTempFileName(); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = sut.List(path); + //---------------Assert----------------------- + result.Should().BeEmpty(); + } + + [TestCase(" ")] + [TestCase("")] + [TestCase(null)] + public void WhenNullOrWhiteSpaceDirectory_ExpectEmptyList(string path) + { + //---------------Arrange------------------- + var sut = new FileSystem(); + //---------------Act---------------------- + var result = sut.List(path); + //---------------Assert----------------------- + result.Should().BeEmpty(); + } + } + + [TestFixture] + class ListAsync + { + [Test] + public async Task WhenDirectoryExist_ExpectContents() + { + //---------------Arrange------------------- + var path = Path.GetTempPath(); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = await sut.ListAsync(path); + //---------------Assert----------------------- + result.Count().Should().BeGreaterThanOrEqualTo(1); + } + + [Test] + public async Task WhenFilePassedIn_ExpectEmptyList() + { + //---------------Arrange------------------- + var path = Path.GetTempFileName(); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = await sut.ListAsync(path); + //---------------Assert----------------------- + result.Should().BeEmpty(); + } + + [TestCase(" ")] + [TestCase("")] + [TestCase(null)] + public async Task WhenNullOrWhiteSpaceDirectory_ExpectEmptyList(string path) + { + //---------------Arrange------------------- + var sut = new FileSystem(); + //---------------Act---------------------- + var result = await sut.ListAsync(path); + //---------------Assert----------------------- + result.Should().BeEmpty(); + } + + [Test] + public async Task ShouldBehaveSameAsSyncVersion() + { + //---------------Arrange------------------- + var path = Path.GetTempPath(); + + var sut = new FileSystem(); + //---------------Act---------------------- + var syncResult = sut.List(path); + var asyncResult = await sut.ListAsync(path); + //---------------Assert----------------------- + asyncResult.Should().BeEquivalentTo(syncResult); + } + } + + [TestFixture] + class Exists + { + [Test] + public void WhenFileDoesNotExist_ExpectFalse() + { + //---------------Arrange------------------- + var path = Create_Missing_File(); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = sut.Exists(path); + //---------------Assert----------------------- + result.Should().BeFalse(); + } + + [Test] + public void WhenFileExist_ExpectTrue() + { + //---------------Arrange------------------- + var path = Create_File(); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = sut.Exists(path); + //---------------Assert----------------------- + result.Should().BeTrue(); + } + + [TestCase(" ")] + [TestCase("")] + [TestCase(null)] + public void WhenFileNullOrWhiteSpace_ExpectTrue(string path) + { + //---------------Arrange------------------- + var sut = new FileSystem(); + //---------------Act---------------------- + var result = sut.Exists(path); + //---------------Assert----------------------- + result.Should().BeFalse(); + } + + [Test] + public void WhenDirectoryExist_ExpectTrue() + { + //---------------Arrange------------------- + var path = Path.GetTempPath(); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = sut.Exists(path); + //---------------Assert----------------------- + result.Should().BeTrue(); + } + } + + [TestFixture] + class ExistsAsync + { + [Test] + public async Task WhenFileDoesNotExist_ExpectFalse() + { + //---------------Arrange------------------- + var path = Create_Missing_File(); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = await sut.ExistsAsync(path); + //---------------Assert----------------------- + result.Should().BeFalse(); + } + + [Test] + public async Task WhenFileExist_ExpectTrue() + { + //---------------Arrange------------------- + var path = Create_File(); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = await sut.ExistsAsync(path); + //---------------Assert----------------------- + result.Should().BeTrue(); + } + + [TestCase(" ")] + [TestCase("")] + [TestCase(null)] + public async Task WhenFileNullOrWhiteSpace_ExpectFalse(string path) + { + //---------------Arrange------------------- + var sut = new FileSystem(); + //---------------Act---------------------- + var result = await sut.ExistsAsync(path); + //---------------Assert----------------------- + result.Should().BeFalse(); + } + + [Test] + public async Task WhenDirectoryExist_ExpectTrue() + { + //---------------Arrange------------------- + var path = Path.GetTempPath(); + + var sut = new FileSystem(); + //---------------Act---------------------- + var result = await sut.ExistsAsync(path); + //---------------Assert----------------------- + result.Should().BeTrue(); + } + + [Test] + public async Task ShouldBehaveSameAsSyncVersion() + { + //---------------Arrange------------------- + var path = Create_File(); + var nonExistingPath = Create_Missing_File(); + + var sut = new FileSystem(); + //---------------Act---------------------- + var syncExistResult = sut.Exists(path); + var asyncExistResult = await sut.ExistsAsync(path); + var syncNonExistResult = sut.Exists(nonExistingPath); + var asyncNonExistResult = await sut.ExistsAsync(nonExistingPath); + //---------------Assert----------------------- + asyncExistResult.Should().Be(syncExistResult); + asyncNonExistResult.Should().Be(syncNonExistResult); + } + } + + [TestFixture] + class Delete + { + [Test] + public void WhenFileExist_ExpectItIsRemoved() + { + //---------------Arrange------------------- + var path = Create_File(); + + var sut = new FileSystem(); + //---------------Act---------------------- + sut.Delete(path); + //---------------Assert----------------------- + var fileExists = File.Exists(path); + fileExists.Should().BeFalse(); + } + + [Test] + public void WhenFileDoesNotExist_ExpectNothingToHappen() + { + //---------------Arrange------------------- + var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + + var sut = new FileSystem(); + //---------------Act---------------------- + sut.Delete(path); + //---------------Assert----------------------- + var fileExists = File.Exists(path); + fileExists.Should().BeFalse(); + } + + [TestCase(" ")] + [TestCase("")] + [TestCase(null)] + public void WhenFileNullOrWhitespace_ExpectNoExceptionsThrown(string path) + { + //---------------Arrange------------------- + var sut = new FileSystem(); + //---------------Act---------------------- + Assert.DoesNotThrow(()=>sut.Delete(path)); + //---------------Assert----------------------- + var fileExists = File.Exists(path); + fileExists.Should().BeFalse(); + } + + [Test] + public void WhenDirectoryExist_ExpectItIsRemoved() + { + //---------------Arrange------------------- + var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(path); + + var sut = new FileSystem(); + //---------------Act---------------------- + sut.Delete(path); + //---------------Assert----------------------- + var fileExists = File.Exists(path); + fileExists.Should().BeFalse(); + } + } + + [TestFixture] + class DeleteAsync + { + [Test] + public async Task WhenFileExist_ExpectItIsRemoved() + { + //---------------Arrange------------------- + var path = Create_File(); + + var sut = new FileSystem(); + //---------------Act---------------------- + await sut.DeleteAsync(path); + //---------------Assert----------------------- + var fileExists = File.Exists(path); + fileExists.Should().BeFalse(); + } + + [Test] + public async Task WhenFileDoesNotExist_ExpectNothingToHappen() + { + //---------------Arrange------------------- + var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + + var sut = new FileSystem(); + //---------------Act---------------------- + await sut.DeleteAsync(path); + //---------------Assert----------------------- + var fileExists = File.Exists(path); + fileExists.Should().BeFalse(); + } + + [TestCase(" ")] + [TestCase("")] + [TestCase(null)] + public async Task WhenFileNullOrWhitespace_ExpectNoExceptionsThrown(string path) + { + //---------------Arrange------------------- + var sut = new FileSystem(); + //---------------Act---------------------- + await sut.DeleteAsync(path); + //---------------Assert----------------------- + var fileExists = File.Exists(path); + fileExists.Should().BeFalse(); + } + + [Test] + public async Task WhenDirectoryExist_ExpectItIsRemoved() + { + //---------------Arrange------------------- + var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(path); + + var sut = new FileSystem(); + //---------------Act---------------------- + await sut.DeleteAsync(path); + //---------------Assert----------------------- + var fileExists = File.Exists(path); + fileExists.Should().BeFalse(); + } + + [Test] + public async Task ShouldBehaveSameAsSyncVersion() + { + //---------------Arrange------------------- + var path1 = Create_File(); + var path2 = Create_File(); + + var sut = new FileSystem(); + //---------------Act---------------------- + sut.Delete(path1); + await sut.DeleteAsync(path2); + //---------------Assert----------------------- + var syncFileExists = File.Exists(path1); + var asyncFileExists = File.Exists(path2); + syncFileExists.Should().BeFalse(); + asyncFileExists.Should().BeFalse(); + } + } + + [TestFixture] + class ReadDocument + { + [Test] + public void GivenFileExist_ExpectDocumentWithBytesReturned() + { + //---------------Arrange------------------- + var contents = "hi, this is some text for a file"; + + var path = Create_File(contents); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = sut.GetDocument(path); + //---------------Assert----------------------- + var expected = "hi, this is some text for a file"; + actual.ToString().Should().Be(expected); + } + + [Test] + public void GivenFileDoesExist_ExpectNullDocument() + { + //---------------Arrange------------------- + var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = sut.GetDocument(path); + //---------------Assert----------------------- + actual.Should().Be(FileSystem.NullDocument); + } + + [Test] + public void GivenNullPath_ExpectNullDocument() + { + //---------------Arrange------------------- + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = sut.GetDocument(null); + //---------------Assert----------------------- + actual.Should().Be(FileSystem.NullDocument); + } + } + + [TestFixture] + class GetDocumentAsync + { + [Test] + public async Task GivenFileExist_ExpectDocumentWithBytesReturned() + { + //---------------Arrange------------------- + var contents = "hi, this is some text for a file"; + + var path = Create_File(contents); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = await sut.GetDocumentAsync(path); + //---------------Assert----------------------- + var expected = "hi, this is some text for a file"; + actual.ToString().Should().Be(expected); + } + + [Test] + public async Task GivenFileDoesExist_ExpectNullDocument() + { + //---------------Arrange------------------- + var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = await sut.GetDocumentAsync(path); + //---------------Assert----------------------- + actual.Should().Be(FileSystem.NullDocument); + } + + [Test] + public async Task GivenNullPath_ExpectNullDocument() + { + //---------------Arrange------------------- + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = await sut.GetDocumentAsync(null); + //---------------Assert----------------------- + actual.Should().Be(FileSystem.NullDocument); + } + + [Test] + public async Task ShouldBehaveSameAsSyncVersion() + { + //---------------Arrange------------------- + var contents = "hi, this is some text for a file"; + var path = Create_File(contents); + + var sut = new FileSystem(); + //---------------Act---------------------- + var syncResult = sut.GetDocument(path); + var asyncResult = await sut.GetDocumentAsync(path); + //---------------Assert----------------------- + asyncResult.Name.Should().Be(syncResult.Name); + asyncResult.Data.Should().BeEquivalentTo(syncResult.Data); + } + } + + [TestFixture] + class Read + { + [Test] + public void GivenFileExist_ExpectDocumentWithBytesReturned() + { + //---------------Arrange------------------- + var contents = "hi, this is some text for a file"; + + var path = Create_File(contents); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = sut.Read(path); + //---------------Assert----------------------- + var expected = "hi, this is some text for a file"; + actual.ToString().Should().Be(expected); + } + + [Test] + public void GivenFileDoesExist_ExpectNullDocument() + { + //---------------Arrange------------------- + var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = sut.Read(path); + //---------------Assert----------------------- + actual.Should().Be(FileSystem.NullDocument); + } + + [Test] + public void GivenNullPath_ExpectNullDocument() + { + //---------------Arrange------------------- + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = sut.Read(null); + //---------------Assert----------------------- + actual.Should().Be(FileSystem.NullDocument); + } + } + + [TestFixture] + class ReadAllLines + { + [Test] + public async Task GivenFileExist_ExpectDocumentWithBytesReturned() + { + //---------------Arrange------------------- + var path = Create_File_With_Million_Lines(); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = await sut.ReadAllLines(path); + //---------------Assert----------------------- + var expectedFirst = "GlobalRank,TldRank,Domain,TLD,RefSubNets,RefIPs,IDN_Domain,IDN_TLD,PrevGlobalRank,PrevTldRank,PrevRefSubNets,PrevRefIPs"; + var expectedLast = "1000000,499336,alexandrevicenzi.com,com,341,364,alexandrevicenzi.com,com,982364,490355,345,368"; + var enumerable = actual as string[] ?? actual.ToArray(); + enumerable.Count().Should().Be(1000001); + enumerable.FirstOrDefault().Should().BeEquivalentTo(expectedFirst); + enumerable.LastOrDefault().Should().BeEquivalentTo(expectedLast); + } + + [Test] + public void GivenFileDoesExist_ExpectException() + { + //---------------Arrange------------------- + var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = Assert.ThrowsAsync(async ()=>await sut.ReadAllLines(path)); + //---------------Assert----------------------- + actual?.Message.Should().NotBeEmpty(); + } + + [Test] + public void GivenNullPath_ExpectException() + { + //---------------Arrange------------------- + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = Assert.ThrowsAsync(async () => await sut.ReadAllLines(null)); + //---------------Assert----------------------- + actual?.Message.Should().NotBeEmpty(); + } + } + + [TestFixture] + class Move + { + [Test] + public void GivenFileExist_ExpectItIsMoved() + { + //---------------Arrange------------------- + var file = Create_File(); + var newFileName = Create_File(); + File.Delete(newFileName); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = sut.Move(file, newFileName); + //---------------Assert----------------------- + var oldFileExist = File.Exists(file); + var newFileExist = File.Exists(newFileName); + + actual.Should().BeTrue(); + oldFileExist.Should().BeFalse(); + newFileExist.Should().BeTrue(); + } + + [Test] + public void GivenFileDoesNotExist_ExpectFalse() + { + //---------------Arrange------------------- + var file = Create_File(); + var newFileName = Create_File(); + File.Delete(newFileName); + File.Delete(file); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = sut.Move(file, newFileName); + //---------------Assert----------------------- + var oldFileExist = File.Exists(file); + var newFileExist = File.Exists(newFileName); + + actual.Should().BeFalse(); + oldFileExist.Should().BeFalse(); + newFileExist.Should().BeFalse(); + } + + [Test] + public void GivenDestinationFileExist_ExpectItIsNotMoved() + { + //---------------Arrange------------------- + var file = Create_File(); + var newFileName = Create_File(); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = sut.Move(file, newFileName); + //---------------Assert----------------------- + var oldFileExist = File.Exists(file); + var newFileExist = File.Exists(newFileName); + + actual.Should().BeFalse(); + oldFileExist.Should().BeTrue(); + newFileExist.Should().BeTrue(); + } + } + + [TestFixture] + class MoveAsync + { + [Test] + public async Task GivenFileExist_ExpectItIsMoved() + { + //---------------Arrange------------------- + var file = Create_File(); + var newFileName = Create_File(); + File.Delete(newFileName); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = await sut.MoveAsync(file, newFileName); + //---------------Assert----------------------- + var oldFileExist = File.Exists(file); + var newFileExist = File.Exists(newFileName); + + actual.Should().BeTrue(); + oldFileExist.Should().BeFalse(); + newFileExist.Should().BeTrue(); + } + + [Test] + public async Task GivenFileDoesNotExist_ExpectFalse() + { + //---------------Arrange------------------- + var file = Create_File(); + var newFileName = Create_File(); + File.Delete(newFileName); + File.Delete(file); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = await sut.MoveAsync(file, newFileName); + //---------------Assert----------------------- + var oldFileExist = File.Exists(file); + var newFileExist = File.Exists(newFileName); + + actual.Should().BeFalse(); + oldFileExist.Should().BeFalse(); + newFileExist.Should().BeFalse(); + } + + [Test] + public async Task GivenDestinationFileExist_ExpectItIsNotMoved() + { + //---------------Arrange------------------- + var file = Create_File(); + var newFileName = Create_File(); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = await sut.MoveAsync(file, newFileName); + //---------------Assert----------------------- + var oldFileExist = File.Exists(file); + var newFileExist = File.Exists(newFileName); + + actual.Should().BeFalse(); + oldFileExist.Should().BeTrue(); + newFileExist.Should().BeTrue(); + } + + [Test] + public async Task ShouldBehaveSameAsSyncVersion() + { + //---------------Arrange------------------- + var file1 = Create_File(); + var file2 = Create_File(); + var newFileName1 = Create_File(); + var newFileName2 = Create_File(); + File.Delete(newFileName1); + File.Delete(newFileName2); + + var sut = new FileSystem(); + //---------------Act---------------------- + var syncResult = sut.Move(file1, newFileName1); + var asyncResult = await sut.MoveAsync(file2, newFileName2); + //---------------Assert----------------------- + syncResult.Should().BeTrue(); + asyncResult.Should().BeTrue(); + + File.Exists(file1).Should().BeFalse(); + File.Exists(file2).Should().BeFalse(); + File.Exists(newFileName1).Should().BeTrue(); + File.Exists(newFileName2).Should().BeTrue(); + } + } + + [TestFixture] + class MoveWithOverwrite + { + [Test] + public void GivenDestinationFileExist_ExpectItIsMoved() + { + //---------------Arrange------------------- + var file = Create_File(); + var newFileName = Create_File(); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = sut.MoveWithOverwrite(file, newFileName); + //---------------Assert----------------------- + var oldFileExist = File.Exists(file); + var newFileExist = File.Exists(newFileName); + + actual.Should().BeTrue(); + oldFileExist.Should().BeFalse(); + newFileExist.Should().BeTrue(); + } + } + + [TestFixture] + class MoveWithOverwriteAsync + { + [Test] + public async Task GivenDestinationFileExist_ExpectItIsMoved() + { + //---------------Arrange------------------- + var file = Create_File(); + var newFileName = Create_File(); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = await sut.MoveWithOverwriteAsync(file, newFileName); + //---------------Assert----------------------- + var oldFileExist = File.Exists(file); + var newFileExist = File.Exists(newFileName); + + actual.Should().BeTrue(); + oldFileExist.Should().BeFalse(); + newFileExist.Should().BeTrue(); + } + + [Test] + public async Task GivenFileDoesNotExist_ExpectFalse() + { + //---------------Arrange------------------- + var file = Create_File(); + var newFileName = Create_File(); + File.Delete(file); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = await sut.MoveWithOverwriteAsync(file, newFileName); + //---------------Assert----------------------- + actual.Should().BeFalse(); + } + + [Test] + public async Task ShouldBehaveSameAsSyncVersion() + { + //---------------Arrange------------------- + var file1 = Create_File("test content 1"); + var file2 = Create_File("test content 2"); + var dest1 = Create_File("original content 1"); + var dest2 = Create_File("original content 2"); + + var sut = new FileSystem(); + //---------------Act---------------------- + var syncResult = sut.MoveWithOverwrite(file1, dest1); + var asyncResult = await sut.MoveWithOverwriteAsync(file2, dest2); + //---------------Assert----------------------- + syncResult.Should().BeTrue(); + asyncResult.Should().BeTrue(); + + File.Exists(file1).Should().BeFalse(); + File.Exists(file2).Should().BeFalse(); + File.Exists(dest1).Should().BeTrue(); + File.Exists(dest2).Should().BeTrue(); + } + } + + [TestFixture] + class Rename + { + [Test] + public void GivenFileExist_ExpectItIsRenamed() + { + //---------------Arrange------------------- + var file = Create_File(); + var newFileName = $"{Guid.NewGuid()}-moved.txt"; + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = sut.Rename(file, newFileName); + //---------------Assert----------------------- + var oldFileExist = File.Exists(file); + var newFileExist = File.Exists(Path.Combine(Path.GetTempPath(), newFileName)); + + actual.Should().BeTrue(); + oldFileExist.Should().BeFalse(); + newFileExist.Should().BeTrue(); + } + + [Test] + public void GivenFileDoesNotExist_ExpectFalse() + { + //---------------Arrange------------------- + var file = Create_File(); + var newFileName = $"{Guid.NewGuid()}-moved.txt"; + File.Delete(file); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = sut.Rename(file, newFileName); + //---------------Assert----------------------- + var oldFileExist = File.Exists(file); + var newFileExist = File.Exists(Path.Combine(Path.GetTempPath(), newFileName)); + + actual.Should().BeFalse(); + oldFileExist.Should().BeFalse(); + newFileExist.Should().BeFalse(); + } + } + + [TestFixture] + class RenameAsync + { + [Test] + public async Task GivenFileExist_ExpectItIsRenamed() + { + //---------------Arrange------------------- + var file = Create_File(); + var newFileName = $"{Guid.NewGuid()}-moved.txt"; + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = await sut.RenameAsync(file, newFileName); + //---------------Assert----------------------- + var oldFileExist = File.Exists(file); + var newFileExist = File.Exists(Path.Combine(Path.GetTempPath(), newFileName)); + + actual.Should().BeTrue(); + oldFileExist.Should().BeFalse(); + newFileExist.Should().BeTrue(); + } + + [Test] + public async Task GivenFileDoesNotExist_ExpectFalse() + { + //---------------Arrange------------------- + var file = Create_File(); + var newFileName = $"{Guid.NewGuid()}-moved.txt"; + File.Delete(file); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = await sut.RenameAsync(file, newFileName); + //---------------Assert----------------------- + var oldFileExist = File.Exists(file); + var newFileExist = File.Exists(Path.Combine(Path.GetTempPath(), newFileName)); + + actual.Should().BeFalse(); + oldFileExist.Should().BeFalse(); + newFileExist.Should().BeFalse(); + } + + [Test] + public async Task ShouldBehaveSameAsSyncVersion() + { + //---------------Arrange------------------- + var file1 = Create_File(); + var file2 = Create_File(); + var newFileName1 = $"{Guid.NewGuid()}-moved.txt"; + var newFileName2 = $"{Guid.NewGuid()}-moved.txt"; + + var sut = new FileSystem(); + //---------------Act---------------------- + var syncResult = sut.Rename(file1, newFileName1); + var asyncResult = await sut.RenameAsync(file2, newFileName2); + //---------------Assert----------------------- + syncResult.Should().BeTrue(); + asyncResult.Should().BeTrue(); + + File.Exists(file1).Should().BeFalse(); + File.Exists(file2).Should().BeFalse(); + File.Exists(Path.Combine(Path.GetTempPath(), newFileName1)).Should().BeTrue(); + File.Exists(Path.Combine(Path.GetTempPath(), newFileName2)).Should().BeTrue(); + } + } + + private static string Create_File_With_Million_Lines() + { + var tmp = Path.GetTempPath(); + var path = Path.Combine(tmp, Guid.NewGuid().ToString()); + + var location = TestContext.CurrentContext.WorkDirectory; + var moveFilePath = Path.Combine(location, "majestic_million.csv"); + File.Move(moveFilePath, path); + + return path; + } + + private static string Create_File(string content) + { + var tmp = Path.GetTempPath(); + var path = Path.Combine(tmp, Guid.NewGuid().ToString()); + + File.WriteAllText(path, content); + + return path; + } + + private static string Create_File() + { + return Create_File(string.Empty); + } + + private static string Create_Missing_File() + { + var tmp = Path.GetTempPath(); + var path = Path.Combine(tmp, Guid.NewGuid().ToString()); + + return path; + } + + private static IDocument Create_CsvFile(string fileName) + { + var csvFile = new DocumentBuilder() + .With_Name(fileName) + .With_Bytes(new byte[5]) + .Create_Document(); + return csvFile; + } + + } +} diff --git a/source/StoneAge.Data.FileSystem/Document.cs b/source/StoneAge.Data.FileSystem/Document.cs index b76b39d..2fc8f5d 100644 --- a/source/StoneAge.Data.FileSystem/Document.cs +++ b/source/StoneAge.Data.FileSystem/Document.cs @@ -1,16 +1,38 @@ -using System.Text; -using StoneAge.FileStore.Domain; - -namespace StoneAge.FileStore -{ - public class Document : IDocument - { - public string Name { get; internal set; } - public byte[] Data { get; internal set; } - - public override string ToString() - { - return Data == null ? string.Empty : Encoding.UTF8.GetString(Data); - } - } -} +using System.Text; +using StoneAge.FileStore.Domain; + +namespace StoneAge.FileStore +{ + /// + /// Represents a file document with name and binary data. + /// Implements the interface. + /// + public class Document : IDocument + { + /// + /// Gets or sets the name of the document. + /// + public string Name { get; internal set; } + + /// + /// Gets or sets the binary data of the document. + /// + public byte[] Data { get; internal set; } + + /// + /// Converts the document's binary data to a string using UTF-8 encoding. + /// + /// + /// A UTF-8 encoded string representation of the document's data, + /// or an empty string if the data is null. + /// + /// + /// This method assumes that the document's data is encoded in UTF-8. + /// If the data uses a different encoding, this method may not return the expected result. + /// + public override string ToString() + { + return Data == null ? string.Empty : Encoding.UTF8.GetString(Data); + } + } +} diff --git a/source/StoneAge.Data.FileSystem/Domain/IDocument.cs b/source/StoneAge.Data.FileSystem/Domain/IDocument.cs index 737e99e..94bf100 100644 --- a/source/StoneAge.Data.FileSystem/Domain/IDocument.cs +++ b/source/StoneAge.Data.FileSystem/Domain/IDocument.cs @@ -1,8 +1,20 @@ -namespace StoneAge.FileStore.Domain -{ - public interface IDocument - { - string Name { get; } - byte[] Data { get; } - } -} +using System.Text; + +namespace StoneAge.FileStore.Domain +{ + /// + /// Defines the interface for a file document, containing the name and binary data. + /// + public interface IDocument + { + /// + /// Gets the name of the document, typically representing the file name. + /// + string Name { get; } + + /// + /// Gets the binary data of the document as a byte array. + /// + byte[] Data { get; } + } +} diff --git a/source/StoneAge.Data.FileSystem/Domain/IFileSystem.cs b/source/StoneAge.Data.FileSystem/Domain/IFileSystem.cs index b9fb8ee..eceb21b 100644 --- a/source/StoneAge.Data.FileSystem/Domain/IFileSystem.cs +++ b/source/StoneAge.Data.FileSystem/Domain/IFileSystem.cs @@ -1,20 +1,144 @@ -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace StoneAge.FileStore.Domain -{ - public interface IFileSystem - { - Task Write(string directory, IDocument file); - Task Append(string directory, IDocument file); - IEnumerable List(string directory); - bool Exists(string path); - void Delete(string path); - IDocument Read(string path); - IDocument GetDocument(string path); - Task> ReadAllLines(string path); - bool Move(string currentPath, string newPath); - bool MoveWithOverwrite(string file, string newLocation); - bool Rename(string filePath, string newName); - } -} +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace StoneAge.FileStore.Domain +{ + /// + /// Defines an interface for file system operations. + /// + public interface IFileSystem + { + /// + /// Writes a document to the specified directory. + /// + /// The directory path where the file should be written. + /// The document to write. + /// A WriteFileResult containing information about the operation. + Task Write(string directory, IDocument file); + + /// + /// Appends data to a file in the specified directory. + /// + /// The directory path where the file should be appended. + /// The document containing data to append. + /// A WriteFileResult containing information about the operation. + Task Append(string directory, IDocument file); + + /// + /// Lists all files in the specified directory. + /// + /// The directory path to list files from. + /// A collection of FileInformation objects representing the files. + IEnumerable List(string directory); + + /// + /// Asynchronously lists all files in the specified directory. + /// + /// The directory path to list files from. + /// A task that represents the asynchronous operation. The task result contains a collection of FileInformation objects representing the files. + Task> ListAsync(string directory); + + /// + /// Checks if a file or directory exists at the specified path. + /// + /// The path to check. + /// True if the file or directory exists, false otherwise. + bool Exists(string path); + + /// + /// Asynchronously checks if a file or directory exists at the specified path. + /// + /// The path to check. + /// A task that represents the asynchronous operation. The task result contains a boolean indicating whether the file or directory exists. + Task ExistsAsync(string path); + + /// + /// Deletes a file or directory at the specified path. + /// + /// The path to delete. + void Delete(string path); + + /// + /// Asynchronously deletes a file or directory at the specified path. + /// + /// The path to delete. + /// A task that represents the asynchronous operation. + Task DeleteAsync(string path); + + /// + /// Reads a document from the specified path. + /// + /// The path to read from. + /// The document read from the path, or NullDocument if the file doesn't exist. + IDocument Read(string path); + + /// + /// Gets a document from the specified path. + /// + /// The path to get the document from. + /// The document from the path, or NullDocument if the file doesn't exist. + IDocument GetDocument(string path); + + /// + /// Asynchronously gets a document from the specified path. + /// + /// The path to get the document from. + /// A task that represents the asynchronous operation. The task result contains the document from the path, or NullDocument if the file doesn't exist. + Task GetDocumentAsync(string path); + + /// + /// Reads all lines from a file at the specified path. + /// + /// The path of the file to read. + /// An enumerable collection of strings representing the lines in the file. + Task> ReadAllLines(string path); + + /// + /// Moves a file from one location to another. + /// + /// The source file path. + /// The destination file path. + /// True if the move was successful, false otherwise. + bool Move(string currentPath, string newPath); + + /// + /// Asynchronously moves a file from one location to another. + /// + /// The source file path. + /// The destination file path. + /// A task that represents the asynchronous operation. The task result contains a boolean indicating whether the move was successful. + Task MoveAsync(string currentPath, string newPath); + + /// + /// Moves a file from one location to another, overwriting the destination if it exists. + /// + /// The source file path. + /// The destination file path. + /// True if the move was successful, false otherwise. + bool MoveWithOverwrite(string file, string newLocation); + + /// + /// Asynchronously moves a file from one location to another, overwriting the destination if it exists. + /// + /// The source file path. + /// The destination file path. + /// A task that represents the asynchronous operation. The task result contains a boolean indicating whether the move was successful. + Task MoveWithOverwriteAsync(string file, string newLocation); + + /// + /// Renames a file. + /// + /// The path of the file to rename. + /// The new name for the file. + /// True if the rename was successful, false otherwise. + bool Rename(string filePath, string newName); + + /// + /// Asynchronously renames a file. + /// + /// The path of the file to rename. + /// The new name for the file. + /// A task that represents the asynchronous operation. The task result contains a boolean indicating whether the rename was successful. + Task RenameAsync(string filePath, string newName); + } +} diff --git a/source/StoneAge.Data.FileSystem/FileSystem.cs b/source/StoneAge.Data.FileSystem/FileSystem.cs index ae57b95..7f9ddcd 100644 --- a/source/StoneAge.Data.FileSystem/FileSystem.cs +++ b/source/StoneAge.Data.FileSystem/FileSystem.cs @@ -1,243 +1,445 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using StoneAge.FileStore.Domain; - -namespace StoneAge.FileStore -{ - public class FileSystem : IFileSystem - { - public static readonly IDocument NullDocument = new Document{Name = "NullDocument"}; - - public async Task Write(string directory, IDocument file) - { - return await Task.Run(() => - { - var result = new WriteFileResult(); - if (string.IsNullOrWhiteSpace(file.Name)) - { - result.ErrorMessages.Add("No file name provided"); - return result; - } - - if (string.IsNullOrWhiteSpace(directory)) - { - result.ErrorMessages.Add("No directory provided"); - return result; - } - - result = Ensure_Directory_Is_Created(directory); - - if (result.HadError) - { - return result; - } - - var filePath = Path.Combine(directory, file.Name); - result = Write_File_To_Path(file, filePath, FileMode.Create); - - return result; - }); - } - - public IEnumerable List(string directory) - { - if (!Directory.Exists(directory)) return new List(); - - var directoryInfo = new DirectoryInfo(directory); - var fileInformations = directoryInfo.GetFiles().Select(info => new FileInformation - { - Name = info.Name, - Size = Convert_Bytes_To_Megabytes(info.Length).ToString() - }); - - double Convert_Bytes_To_Megabytes(long bytes) - { - var fileSizeInKB = bytes / 1024; - var fileSizeInMB = fileSizeInKB / 1024; - return fileSizeInMB; - } - - return fileInformations; - } - - public bool Exists(string path) - { - if (string.IsNullOrEmpty(path)) - { - return false; - } - - return Directory.Exists(path) || File.Exists(path); - } - - public void Delete(string path) - { - if (Directory.Exists(path)) - { - Directory.Delete(path, true); - return; - } - - if (File.Exists(path)) - { - File.Delete(path); - } - } - - public IDocument Read(string path) - { - return GetDocument(path); - } - - public IDocument GetDocument(string path) - { - if (!File.Exists(path)) - { - return NullDocument; - } - - var name = Path.GetFileName(path); - return new DocumentBuilder() - .With_Name(name) - .With_File(path) - .Create_Document(); - } - - public bool Move(string file, string newLocation) - { - if (!File.Exists(file)) - { - return false; - } - - try - { - File.Move(file, newLocation); - return true; - } - catch (Exception) - { - return false; - } - - } - - public bool MoveWithOverwrite(string file, string newLocation) - { - if (!File.Exists(file)) - { - return false; - } - - if (File.Exists(newLocation)) - { - File.Delete(newLocation); - } - - File.Move(file, newLocation); - return true; - - } - - public bool Rename(string file, string newFileName) - { - if (!File.Exists(file)) - { - return false; - } - - var directoryPath = Path.GetDirectoryName(file); - var newFilePath = Path.Combine(directoryPath, newFileName); - - File.Move(file, newFilePath); - - return true; - } - - public async Task Append(string directory, IDocument file) - { - return await Task.Run(() => - { - var result = new WriteFileResult(); - if (string.IsNullOrWhiteSpace(file.Name)) - { - result.ErrorMessages.Add("No file name provided"); - return result; - } - - if (string.IsNullOrWhiteSpace(directory)) - { - result.ErrorMessages.Add("No directory provided"); - return result; - } - - result = Ensure_Directory_Is_Created(directory); - - if (result.HadError) - { - return result; - } - - var filePath = Path.Combine(directory, file.Name); - result = Write_File_To_Path(file, filePath, FileMode.Append); - - return result; - }); - } - - public async Task> ReadAllLines(string path) - { - return await File.ReadAllLinesAsync(path); - } - - private WriteFileResult Write_File_To_Path(IDocument file, string filePath, FileMode fileMode) - { - var result = new WriteFileResult(); - - if (file.Data == null) - { - result.ErrorMessages.Add("No file data provided; cannot write file."); - return result; - } - - try - { - using (var stream = new FileStream(filePath, fileMode)) - { - stream.Write(file.Data); - } - - result.FullFilePath = filePath; - } - catch (Exception e) - { - result.ErrorMessages.Add($"An error occured writing the file [{e.Message}]"); - } - - return result; - } - - private WriteFileResult Ensure_Directory_Is_Created(string currentDirectory) - { - var result= new WriteFileResult(); - if (!Directory.Exists(currentDirectory)) - { - try - { - Directory.CreateDirectory(currentDirectory); - } - catch (Exception e) - { - result.ErrorMessages.Add($"An error occured creating directory structure [{e.Message}]"); - return result; - } - } - - return result; - } - - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using StoneAge.FileStore.Domain; + +namespace StoneAge.FileStore +{ + /// + /// A concrete implementation of the IFileSystem interface that interacts with the actual file system. + /// + public class FileSystem : IFileSystem + { + /// + /// A null document instance to be returned when a document cannot be found. + /// + public static readonly IDocument NullDocument = new Document{Name = "NullDocument"}; + + /// + /// Writes a document to the specified directory. + /// + /// The directory path where the file should be written. + /// The document to write. + /// A WriteFileResult containing information about the operation. + public async Task Write(string directory, IDocument file) + { + return await Task.Run(() => + { + var result = new WriteFileResult(); + if (string.IsNullOrWhiteSpace(file.Name)) + { + result.ErrorMessages.Add("No file name provided"); + return result; + } + + if (string.IsNullOrWhiteSpace(directory)) + { + result.ErrorMessages.Add("No directory provided"); + return result; + } + + // Validate that the file name doesn't contain path separators + if (file.Name.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0) + { + result.ErrorMessages.Add("File name contains invalid characters"); + return result; + } + + result = Ensure_Directory_Is_Created(directory); + + if (result.HadError) + { + return result; + } + + var filePath = Path.Combine(directory, file.Name); + result = Write_File_To_Path(file, filePath, FileMode.Create); + + return result; + }); + } + + /// + /// Lists all files in the specified directory. + /// + /// The directory path to list files from. + /// A collection of FileInformation objects representing the files. + public IEnumerable List(string directory) + { + if (string.IsNullOrWhiteSpace(directory) || !Directory.Exists(directory)) + return new List(); + + var directoryInfo = new DirectoryInfo(directory); + var fileInformations = directoryInfo.GetFiles().Select(info => new FileInformation + { + Name = info.Name, + Size = Convert_Bytes_To_Megabytes(info.Length).ToString() + }); + + double Convert_Bytes_To_Megabytes(long bytes) + { + var fileSizeInKB = bytes / 1024; + var fileSizeInMB = fileSizeInKB / 1024; + return fileSizeInMB; + } + + return fileInformations; + } + + /// + /// Asynchronously lists all files in the specified directory. + /// + /// The directory path to list files from. + /// A task that represents the asynchronous operation. The task result contains a collection of FileInformation objects representing the files. + public async Task> ListAsync(string directory) + { + return await Task.Run(() => List(directory)); + } + + /// + /// Checks if a file or directory exists at the specified path. + /// + /// The path to check. + /// True if the file or directory exists, false otherwise. + public bool Exists(string path) + { + if (string.IsNullOrWhiteSpace(path)) + { + return false; + } + + return Directory.Exists(path) || File.Exists(path); + } + + /// + /// Asynchronously checks if a file or directory exists at the specified path. + /// + /// The path to check. + /// A task that represents the asynchronous operation. The task result contains a boolean indicating whether the file or directory exists. + public async Task ExistsAsync(string path) + { + return await Task.Run(() => Exists(path)); + } + + /// + /// Deletes a file or directory at the specified path. + /// + /// The path to delete. + public void Delete(string path) + { + if (string.IsNullOrWhiteSpace(path)) + { + return; + } + + if (Directory.Exists(path)) + { + Directory.Delete(path, true); + return; + } + + if (File.Exists(path)) + { + File.Delete(path); + } + } + + /// + /// Asynchronously deletes a file or directory at the specified path. + /// + /// The path to delete. + /// A task that represents the asynchronous operation. + public async Task DeleteAsync(string path) + { + await Task.Run(() => Delete(path)); + } + + /// + /// Reads a document from the specified path. + /// + /// The path to read from. + /// The document read from the path, or NullDocument if the file doesn't exist. + public IDocument Read(string path) + { + return GetDocument(path); + } + + /// + /// Gets a document from the specified path. + /// + /// The path to get the document from. + /// The document from the path, or NullDocument if the file doesn't exist. + /// + /// The document's content is assumed to be in UTF-8 encoding when converted to string. + /// + public IDocument GetDocument(string path) + { + if (string.IsNullOrWhiteSpace(path) || !File.Exists(path)) + { + return NullDocument; + } + + var name = Path.GetFileName(path); + return new DocumentBuilder() + .With_Name(name) + .With_File(path) + .Create_Document(); + } + + /// + /// Asynchronously gets a document from the specified path. + /// + /// The path to get the document from. + /// A task that represents the asynchronous operation. The task result contains the document from the path, or NullDocument if the file doesn't exist. + public async Task GetDocumentAsync(string path) + { + return await Task.Run(() => GetDocument(path)); + } + + /// + /// Moves a file from one location to another. + /// + /// The source file path. + /// The destination file path. + /// True if the move was successful, false otherwise. + public bool Move(string file, string newLocation) + { + if (string.IsNullOrWhiteSpace(file) || string.IsNullOrWhiteSpace(newLocation) || !File.Exists(file)) + { + return false; + } + + try + { + File.Move(file, newLocation); + return true; + } + catch (Exception) + { + return false; + } + } + + /// + /// Asynchronously moves a file from one location to another. + /// + /// The source file path. + /// The destination file path. + /// A task that represents the asynchronous operation. The task result contains a boolean indicating whether the move was successful. + public async Task MoveAsync(string file, string newLocation) + { + return await Task.Run(() => Move(file, newLocation)); + } + + /// + /// Moves a file from one location to another, overwriting the destination if it exists. + /// + /// The source file path. + /// The destination file path. + /// True if the move was successful, false otherwise. + public bool MoveWithOverwrite(string file, string newLocation) + { + if (string.IsNullOrWhiteSpace(file) || string.IsNullOrWhiteSpace(newLocation) || !File.Exists(file)) + { + return false; + } + + try + { + if (File.Exists(newLocation)) + { + File.Delete(newLocation); + } + + File.Move(file, newLocation); + return true; + } + catch (Exception) + { + return false; + } + } + + /// + /// Asynchronously moves a file from one location to another, overwriting the destination if it exists. + /// + /// The source file path. + /// The destination file path. + /// A task that represents the asynchronous operation. The task result contains a boolean indicating whether the move was successful. + public async Task MoveWithOverwriteAsync(string file, string newLocation) + { + return await Task.Run(() => MoveWithOverwrite(file, newLocation)); + } + + /// + /// Renames a file. + /// + /// The path of the file to rename. + /// The new name for the file. + /// True if the rename was successful, false otherwise. + public bool Rename(string filePath, string newName) + { + if (string.IsNullOrWhiteSpace(filePath) || string.IsNullOrWhiteSpace(newName) || !File.Exists(filePath)) + { + return false; + } + + try + { + // Validate that the new name doesn't contain path separators + if (newName.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0) + { + return false; + } + + var directoryPath = Path.GetDirectoryName(filePath); + var newFilePath = Path.Combine(directoryPath, newName); + + File.Move(filePath, newFilePath); + return true; + } + catch (Exception) + { + return false; + } + } + + /// + /// Asynchronously renames a file. + /// + /// The path of the file to rename. + /// The new name for the file. + /// A task that represents the asynchronous operation. The task result contains a boolean indicating whether the rename was successful. + public async Task RenameAsync(string filePath, string newName) + { + return await Task.Run(() => Rename(filePath, newName)); + } + + /// + /// Appends data to a file in the specified directory. + /// + /// The directory path where the file should be appended. + /// The document containing data to append. + /// A WriteFileResult containing information about the operation. + public async Task Append(string directory, IDocument file) + { + return await Task.Run(() => + { + var result = new WriteFileResult(); + if (string.IsNullOrWhiteSpace(file.Name)) + { + result.ErrorMessages.Add("No file name provided"); + return result; + } + + if (string.IsNullOrWhiteSpace(directory)) + { + result.ErrorMessages.Add("No directory provided"); + return result; + } + + // Validate that the file name doesn't contain path separators + if (file.Name.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0) + { + result.ErrorMessages.Add("File name contains invalid characters"); + return result; + } + + result = Ensure_Directory_Is_Created(directory); + + if (result.HadError) + { + return result; + } + + var filePath = Path.Combine(directory, file.Name); + result = Write_File_To_Path(file, filePath, FileMode.Append); + + return result; + }); + } + + /// + /// Reads all lines from a file at the specified path. + /// + /// The path of the file to read. + /// An enumerable collection of strings representing the lines in the file. + /// + /// This method assumes the file is encoded in the default encoding for the system. + /// + public async Task> ReadAllLines(string path) + { + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentNullException(nameof(path), "Path cannot be null or empty"); + } + + if (!File.Exists(path)) + { + throw new FileNotFoundException($"File not found: {path}", path); + } + + return await File.ReadAllLinesAsync(path); + } + + /// + /// Writes file data to the specified path with the given file mode. + /// + /// The document to write. + /// The path to write to. + /// The file mode to use (Create or Append). + /// A WriteFileResult containing information about the operation. + private WriteFileResult Write_File_To_Path(IDocument file, string filePath, FileMode fileMode) + { + var result = new WriteFileResult(); + + if (file.Data == null) + { + result.ErrorMessages.Add("No file data provided; cannot write file."); + return result; + } + + try + { + // Modern using declaration (C# 8.0+) + using var stream = new FileStream(filePath, fileMode); + stream.Write(file.Data); + + result.FullFilePath = filePath; + } + catch (Exception e) + { + result.ErrorMessages.Add($"An error occured writing the file [{e.Message}]"); + } + + return result; + } + + /// + /// Ensures that the specified directory exists, creating it if necessary. + /// + /// The directory path to check or create. + /// A WriteFileResult containing information about the operation. + private WriteFileResult Ensure_Directory_Is_Created(string currentDirectory) + { + var result= new WriteFileResult(); + if (!Directory.Exists(currentDirectory)) + { + try + { + Directory.CreateDirectory(currentDirectory); + } + catch (Exception e) + { + result.ErrorMessages.Add($"An error occured creating directory structure [{e.Message}]"); + return result; + } + } + + return result; + } + + } +}