From 7591bbc664ad69f7c4617d87bd30412ac950c758 Mon Sep 17 00:00:00 2001 From: darthsharp <48331467+darthsharp@users.noreply.github.com> Date: Fri, 26 Dec 2025 12:19:12 +0100 Subject: [PATCH 1/3] Add support for directory and file path validation utilities and register file system services - Introduced `DirectoryExtensions` with methods for ensuring directory existence. - Added `IOServiceCollectionExtensions` to enable dependency injection for file system abstractions. --- .../IO/DirectoryExtensions.cs | 34 +++++++++++++++++++ .../IO/IOServiceCollectionExtensions.cs | 18 ++++++++++ 2 files changed, 52 insertions(+) create mode 100644 source/Core/CreativeCoders.Core/IO/DirectoryExtensions.cs create mode 100644 source/Core/CreativeCoders.Core/IO/IOServiceCollectionExtensions.cs diff --git a/source/Core/CreativeCoders.Core/IO/DirectoryExtensions.cs b/source/Core/CreativeCoders.Core/IO/DirectoryExtensions.cs new file mode 100644 index 00000000..1345705e --- /dev/null +++ b/source/Core/CreativeCoders.Core/IO/DirectoryExtensions.cs @@ -0,0 +1,34 @@ +using System.IO.Abstractions; + +#nullable enable + +namespace CreativeCoders.Core.IO; + +public static class DirectoryExtensions +{ + public static void EnsureDirectoryExists(this IDirectory directory, string? directoryPath) + { + if (string.IsNullOrWhiteSpace(directoryPath)) + { + return; + } + + directory.CreateDirectory(directoryPath); + } + + public static void EnsureDirectoryForFileNameExists(this IDirectory directory, string? filePath) + { + if (string.IsNullOrWhiteSpace(filePath)) + { + return; + } + + var directoryPath = directory.FileSystem.Path.GetDirectoryName(filePath); + if (string.IsNullOrWhiteSpace(directoryPath)) + { + return; + } + + directory.CreateDirectory(directoryPath); + } +} diff --git a/source/Core/CreativeCoders.Core/IO/IOServiceCollectionExtensions.cs b/source/Core/CreativeCoders.Core/IO/IOServiceCollectionExtensions.cs new file mode 100644 index 00000000..c592a0a4 --- /dev/null +++ b/source/Core/CreativeCoders.Core/IO/IOServiceCollectionExtensions.cs @@ -0,0 +1,18 @@ +using System.Diagnostics.CodeAnalysis; +using System.IO.Abstractions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace CreativeCoders.Core.IO; + +[ExcludeFromCodeCoverage] +public static class IOServiceCollectionExtensions +{ + public static IServiceCollection AddFileSystem(this IServiceCollection services) + { + services.TryAddSingleton(); + services.TryAddSingleton(); + + return services; + } +} From 5ae72d85b76c44762508f11ea36cc6ce785c30e2 Mon Sep 17 00:00:00 2001 From: darthsharp <48331467+darthsharp@users.noreply.github.com> Date: Fri, 26 Dec 2025 14:40:00 +0100 Subject: [PATCH 2/3] Add unit tests for `DirectoryExtensions` to validate directory creation logic - Introduced comprehensive tests for `EnsureDirectoryExists` and `EnsureDirectoryForFileNameExists`. - Covered edge cases for null, empty, and whitespace paths. --- .../IO/DirectoryExtensionsTests.cs | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 tests/CreativeCoders.Core.UnitTests/IO/DirectoryExtensionsTests.cs diff --git a/tests/CreativeCoders.Core.UnitTests/IO/DirectoryExtensionsTests.cs b/tests/CreativeCoders.Core.UnitTests/IO/DirectoryExtensionsTests.cs new file mode 100644 index 00000000..d2da26bd --- /dev/null +++ b/tests/CreativeCoders.Core.UnitTests/IO/DirectoryExtensionsTests.cs @@ -0,0 +1,107 @@ +using System.IO.Abstractions; +using CreativeCoders.Core.IO; +using FakeItEasy; +using AwesomeAssertions; +using Xunit; + +#nullable enable +namespace CreativeCoders.Core.UnitTests.IO; + +public class DirectoryExtensionsTests +{ + [Fact] + public void EnsureDirectoryExists_ValidPath_CreatesDirectory() + { + // Arrange + var directory = A.Fake(); + const string path = "/test/path"; + + // Act + directory.EnsureDirectoryExists(path); + + // Assert + A.CallTo(() => directory.CreateDirectory(path)) + .MustHaveHappenedOnceExactly(); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public void EnsureDirectoryExists_NullOrEmptyPath_DoesNotCreateDirectory(string? path) + { + // Arrange + var directory = A.Fake(); + + // Act + directory.EnsureDirectoryExists(path); + + // Assert + A.CallTo(() => directory.CreateDirectory(A._)) + .MustNotHaveHappened(); + } + + [Fact] + public void EnsureDirectoryForFileNameExists_ValidFilePath_CreatesDirectory() + { + // Arrange + var directory = A.Fake(); + var fileSystem = A.Fake(); + var pathMock = A.Fake(); + + A.CallTo(() => directory.FileSystem).Returns(fileSystem); + A.CallTo(() => fileSystem.Path).Returns(pathMock); + + const string filePath = "/test/path/file.txt"; + const string directoryPath = "/test/path"; + + A.CallTo(() => pathMock.GetDirectoryName(filePath)).Returns(directoryPath); + + // Act + directory.EnsureDirectoryForFileNameExists(filePath); + + // Assert + A.CallTo(() => directory.CreateDirectory(directoryPath)) + .MustHaveHappenedOnceExactly(); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public void EnsureDirectoryForFileNameExists_NullOrEmptyFilePath_DoesNotCreateDirectory(string? filePath) + { + // Arrange + var directory = A.Fake(); + + // Act + directory.EnsureDirectoryForFileNameExists(filePath); + + // Assert + A.CallTo(() => directory.CreateDirectory(A._)) + .MustNotHaveHappened(); + } + + [Fact] + public void EnsureDirectoryForFileNameExists_GetDirectoryNameReturnsNull_DoesNotCreateDirectory() + { + // Arrange + var directory = A.Fake(); + var fileSystem = A.Fake(); + var pathMock = A.Fake(); + + A.CallTo(() => directory.FileSystem).Returns(fileSystem); + A.CallTo(() => fileSystem.Path).Returns(pathMock); + + const string filePath = "file.txt"; + + A.CallTo(() => pathMock.GetDirectoryName(filePath)).Returns(null); + + // Act + directory.EnsureDirectoryForFileNameExists(filePath); + + // Assert + A.CallTo(() => directory.CreateDirectory(A._)) + .MustNotHaveHappened(); + } +} From 5752e5835ee2af3036e2d7b9c832b4fa00f839e0 Mon Sep 17 00:00:00 2001 From: darthsharp <48331467+darthsharp@users.noreply.github.com> Date: Fri, 26 Dec 2025 14:41:15 +0100 Subject: [PATCH 3/3] Suppress nullable warnings in `EnumerableStringExtensions` and adjust null-forgiving operator usage in `ToDictionary` method --- .../CreativeCoders.Core/Text/EnumerableStringExtensions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/Core/CreativeCoders.Core/Text/EnumerableStringExtensions.cs b/source/Core/CreativeCoders.Core/Text/EnumerableStringExtensions.cs index f3251fe8..695924c8 100644 --- a/source/Core/CreativeCoders.Core/Text/EnumerableStringExtensions.cs +++ b/source/Core/CreativeCoders.Core/Text/EnumerableStringExtensions.cs @@ -10,6 +10,7 @@ namespace CreativeCoders.Core.Text; public static class EnumerableStringExtensions { + [SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")] public static Dictionary ToDictionary(this IEnumerable items, string separator, bool ignoreInvalidEntries = true) { @@ -27,7 +28,7 @@ public static Dictionary ToDictionary(this IEnumerable i return x != null; }) - .ToDictionary(x => x.Key, x => x.Value); + .ToDictionary(x => x!.Key, x => x!.Value); } [ExcludeFromCodeCoverage]