From cedcdc0330a9a0b95ae06e92ac4bfb48064b3121 Mon Sep 17 00:00:00 2001 From: Travis Frisinger Date: Thu, 29 May 2025 13:50:16 -0600 Subject: [PATCH] cursor version of factory.ai refactoring - took a lot more prompting to get to the same place and not as nice of code --- .../FileSystemAsyncTests.cs | 459 ++++++++++++++++++ .../FileSystemTests.cs | 88 +++- .../Domain/IFileSystem.cs | 8 + source/StoneAge.Data.FileSystem/FileSystem.cs | 90 +++- 4 files changed, 620 insertions(+), 25 deletions(-) create mode 100644 source/StoneAge.Data.FileSystem.Tests/FileSystemAsyncTests.cs diff --git a/source/StoneAge.Data.FileSystem.Tests/FileSystemAsyncTests.cs b/source/StoneAge.Data.FileSystem.Tests/FileSystemAsyncTests.cs new file mode 100644 index 0000000..063f1b0 --- /dev/null +++ b/source/StoneAge.Data.FileSystem.Tests/FileSystemAsyncTests.cs @@ -0,0 +1,459 @@ +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 FileSystemAsyncTests + { + [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(); + } + } + + [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(); + } + } + + [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 & Assert----------------------- + await sut.DeleteAsync(path); + 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 = Directory.Exists(path); + fileExists.Should().BeFalse(); + } + } + + [TestFixture] + class ReadAsync + { + [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.ReadAsync(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.ReadAsync(path); + //---------------Assert----------------------- + actual.Should().Be(FileSystem.NullDocument); + } + + [Test] + public async Task GivenNullPath_ExpectNullDocument() + { + //---------------Arrange------------------- + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = await sut.ReadAsync(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); + } + } + + [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(); + } + } + + [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----------------------- + var oldFileExist = File.Exists(file); + var newFileExist = File.Exists(newFileName); + + actual.Should().BeFalse(); + oldFileExist.Should().BeFalse(); + newFileExist.Should().BeTrue(); // Destination file should still exist + } + } + + [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 GivenDestinationFileAlreadyExists_ExpectFalse() + { + //---------------Arrange------------------- + var sourceFile = Create_File("source content"); + var existingFileName = Guid.NewGuid().ToString(); + var destinationFilePath = Path.Combine(Path.GetTempPath(), existingFileName); + File.WriteAllText(destinationFilePath, "destination content"); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = await sut.RenameAsync(sourceFile, existingFileName); + //---------------Assert----------------------- + var sourceFileExists = File.Exists(sourceFile); + var destFileExists = File.Exists(destinationFilePath); + + actual.Should().BeFalse(); + sourceFileExists.Should().BeTrue(); // Source file should still exist + destFileExists.Should().BeTrue(); // Destination file should still exist + + // Verify content hasn't changed + var destContent = File.ReadAllText(destinationFilePath); + destContent.Should().Be("destination content"); + } + } + + 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; + } + } +} \ No newline at end of file diff --git a/source/StoneAge.Data.FileSystem.Tests/FileSystemTests.cs b/source/StoneAge.Data.FileSystem.Tests/FileSystemTests.cs index 77d96a4..27f233c 100644 --- a/source/StoneAge.Data.FileSystem.Tests/FileSystemTests.cs +++ b/source/StoneAge.Data.FileSystem.Tests/FileSystemTests.cs @@ -41,7 +41,7 @@ public async Task WhenFileAndPathContainNewSubDirectories_ExpectFileWritten() var sut = new FileSystem(); //---------------Act---------------------- - var result = await sut.Write(path, document); + var result = await sut.Append(path, document); //---------------Assert----------------------- var fileWritten = File.Exists(Path.Combine(path, fileName)); result.HadError.Should().BeFalse(); @@ -167,7 +167,7 @@ public async Task WhenFileAndPathContainNewSubDirectories_ExpectFileWritten() var sut = new FileSystem(); //---------------Act---------------------- - var result = await sut.Write(path, document); + var result = await sut.Append(path, document); //---------------Assert----------------------- var fileWritten = File.Exists(Path.Combine(path, fileName)); result.HadError.Should().BeFalse(); @@ -184,7 +184,7 @@ public async Task WhenPathContainsNullOrWhiteSpace_ExpectErrorMessage(string pat var sut = new FileSystem(); //---------------Act---------------------- - var result = await sut.Write(path, document); + var result = await sut.Append(path, document); //---------------Assert----------------------- result.HadError.Should().BeTrue(); } @@ -198,7 +198,7 @@ public async Task WhenRelativePath_ExpectFileWritten(string path) var sut = new FileSystem(); //---------------Act---------------------- - var result = await sut.Write(path, document); + var result = await sut.Append(path, document); //---------------Assert----------------------- var fileWritten = File.Exists(Path.Combine(path, "test.csv")); result.HadError.Should().BeFalse(); @@ -215,11 +215,30 @@ public async Task WhenFileAndPathValid_ExpectFullFilePathReturned() var sut = new FileSystem(); //---------------Act---------------------- - var result = await sut.Write(path, document); + var result = await sut.Append(path, document); //---------------Assert----------------------- var expected = Path.Combine(path, fileName); result.FullFilePath.Should().Be(expected); } + + [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.Append(path, document); + //---------------Assert----------------------- + result.HadError.Should().BeTrue(); + result.ErrorMessages.Should().Contain("No file data provided; cannot write file."); + } } [TestFixture] @@ -297,7 +316,7 @@ public void WhenFileExist_ExpectTrue() [TestCase(" ")] [TestCase("")] [TestCase(null)] - public void WhenFileNullOrWhiteSpace_ExpectTrue(string path) + public void WhenFileNullOrWhiteSpace_ExpectFalse(string path) { //---------------Arrange------------------- var sut = new FileSystem(); @@ -543,21 +562,13 @@ public void GivenFileExist_ExpectItIsMoved() public void GivenFileDoesNotExist_ExpectFalse() { //---------------Arrange------------------- - var file = Create_File(); - var newFileName = Create_File(); - File.Delete(newFileName); - File.Delete(file); + var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); var sut = new FileSystem(); //---------------Act---------------------- - var actual = sut.Move(file, newFileName); + var actual = sut.Move(path, Path.Combine(path, Guid.NewGuid().ToString())); //---------------Assert----------------------- - var oldFileExist = File.Exists(file); - var newFileExist = File.Exists(newFileName); - actual.Should().BeFalse(); - oldFileExist.Should().BeFalse(); - newFileExist.Should().BeFalse(); } [Test] @@ -601,6 +612,26 @@ public void GivenDestinationFileExist_ExpectItIsMoved() oldFileExist.Should().BeFalse(); newFileExist.Should().BeTrue(); } + + [Test] + public void GivenFileDoesNotExist_ExpectFalse() + { + //---------------Arrange------------------- + var file = Create_File(); + var newFileName = Create_File(); + File.Delete(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().BeFalse(); + oldFileExist.Should().BeFalse(); + newFileExist.Should().BeTrue(); // Destination file should still exist + } } [TestFixture] @@ -644,6 +675,31 @@ public void GivenFileDoesNotExist_ExpectFalse() oldFileExist.Should().BeFalse(); newFileExist.Should().BeFalse(); } + + [Test] + public void GivenDestinationFileAlreadyExists_ExpectFalse() + { + //---------------Arrange------------------- + var sourceFile = Create_File("source content"); + var existingFileName = Guid.NewGuid().ToString(); + var destinationFilePath = Path.Combine(Path.GetTempPath(), existingFileName); + File.WriteAllText(destinationFilePath, "destination content"); + + var sut = new FileSystem(); + //---------------Act---------------------- + var actual = sut.Rename(sourceFile, existingFileName); + //---------------Assert----------------------- + var sourceFileExists = File.Exists(sourceFile); + var destFileExists = File.Exists(destinationFilePath); + + actual.Should().BeFalse(); + sourceFileExists.Should().BeTrue(); // Source file should still exist + destFileExists.Should().BeTrue(); // Destination file should still exist + + // Verify content hasn't changed + var destContent = File.ReadAllText(destinationFilePath); + destContent.Should().Be("destination content"); + } } private static string Create_File_With_Million_Lines() diff --git a/source/StoneAge.Data.FileSystem/Domain/IFileSystem.cs b/source/StoneAge.Data.FileSystem/Domain/IFileSystem.cs index b9fb8ee..8062358 100644 --- a/source/StoneAge.Data.FileSystem/Domain/IFileSystem.cs +++ b/source/StoneAge.Data.FileSystem/Domain/IFileSystem.cs @@ -8,13 +8,21 @@ public interface IFileSystem Task Write(string directory, IDocument file); Task Append(string directory, IDocument file); IEnumerable List(string directory); + Task> ListAsync(string directory); bool Exists(string path); + Task ExistsAsync(string path); void Delete(string path); + Task DeleteAsync(string path); IDocument Read(string path); + Task ReadAsync(string path); IDocument GetDocument(string path); + Task GetDocumentAsync(string path); Task> ReadAllLines(string path); bool Move(string currentPath, string newPath); + Task MoveAsync(string currentPath, string newPath); bool MoveWithOverwrite(string file, string newLocation); + Task MoveWithOverwriteAsync(string file, string newLocation); bool Rename(string filePath, string newName); + Task RenameAsync(string filePath, string newName); } } diff --git a/source/StoneAge.Data.FileSystem/FileSystem.cs b/source/StoneAge.Data.FileSystem/FileSystem.cs index ae57b95..3648bf4 100644 --- a/source/StoneAge.Data.FileSystem/FileSystem.cs +++ b/source/StoneAge.Data.FileSystem/FileSystem.cs @@ -63,6 +63,11 @@ double Convert_Bytes_To_Megabytes(long bytes) return fileInformations; } + public async Task> ListAsync(string directory) + { + return await Task.Run(() => List(directory)); + } + public bool Exists(string path) { if (string.IsNullOrEmpty(path)) @@ -73,8 +78,18 @@ public bool Exists(string path) return Directory.Exists(path) || File.Exists(path); } + public async Task ExistsAsync(string path) + { + return await Task.Run(() => Exists(path)); + } + public void Delete(string path) { + if (string.IsNullOrWhiteSpace(path)) + { + return; + } + if (Directory.Exists(path)) { Directory.Delete(path, true); @@ -87,11 +102,21 @@ public void Delete(string path) } } + public async Task DeleteAsync(string path) + { + await Task.Run(() => Delete(path)); + } + public IDocument Read(string path) { return GetDocument(path); } + public async Task ReadAsync(string path) + { + return await GetDocumentAsync(path); + } + public IDocument GetDocument(string path) { if (!File.Exists(path)) @@ -106,6 +131,11 @@ public IDocument GetDocument(string path) .Create_Document(); } + public async Task GetDocumentAsync(string path) + { + return await Task.Run(() => GetDocument(path)); + } + public bool Move(string file, string newLocation) { if (!File.Exists(file)) @@ -125,6 +155,11 @@ public bool Move(string file, string newLocation) } + public async Task MoveAsync(string file, string newLocation) + { + return await Task.Run(() => Move(file, newLocation)); + } + public bool MoveWithOverwrite(string file, string newLocation) { if (!File.Exists(file)) @@ -132,14 +167,25 @@ public bool MoveWithOverwrite(string file, string newLocation) return false; } - if (File.Exists(newLocation)) + try { - File.Delete(newLocation); - } + if (File.Exists(newLocation)) + { + File.Delete(newLocation); + } - File.Move(file, newLocation); - return true; + File.Move(file, newLocation); + return true; + } + catch (Exception) + { + return false; + } + } + public async Task MoveWithOverwriteAsync(string file, string newLocation) + { + return await Task.Run(() => MoveWithOverwrite(file, newLocation)); } public bool Rename(string file, string newFileName) @@ -149,12 +195,28 @@ public bool Rename(string file, string newFileName) return false; } - var directoryPath = Path.GetDirectoryName(file); - var newFilePath = Path.Combine(directoryPath, newFileName); + try + { + var directoryPath = Path.GetDirectoryName(file); + var newFilePath = Path.Combine(directoryPath, newFileName); - File.Move(file, newFilePath); + if (File.Exists(newFilePath)) + { + return false; + } - return true; + File.Move(file, newFilePath); + return true; + } + catch (Exception) + { + return false; + } + } + + public async Task RenameAsync(string file, string newFileName) + { + return await Task.Run(() => Rename(file, newFileName)); } public async Task Append(string directory, IDocument file) @@ -190,6 +252,16 @@ public async Task Append(string directory, IDocument file) public async Task> ReadAllLines(string path) { + if (string.IsNullOrEmpty(path)) + { + throw new ArgumentNullException(nameof(path)); + } + + if (!File.Exists(path)) + { + throw new FileNotFoundException($"File not found: {path}", path); + } + return await File.ReadAllLinesAsync(path); }