From 687756d05f3d5dc0423ba6de777d5e483b3cc43a Mon Sep 17 00:00:00 2001 From: Philippe Matray Date: Sat, 4 Feb 2023 13:12:15 +0100 Subject: [PATCH] Introduce VFSException class --- docs/api/VFSException.VFSException(string).md | 17 +++++++ ...xception.VFSException(string,Exception).md | 25 +++++++++ docs/api/VFSException.md | 17 +++++++ docs/api/VirtualFileSystem.md | 8 +++ docs/links | 4 ++ .../Abstractions/VFSPath.cs | 51 ++++++++++++++++--- ....VirtualFileSystem.Core.csproj.DotSettings | 3 ++ .../Exceptions/VFSException.cs | 36 +++++++++++++ .../GlobalUsings.cs | 1 + src/Atypical.VirtualFileSystem.Core/VFS.cs | 45 ++++++++++++++-- .../ValueObjects/VFSDirectoryPath.cs | 9 +++- .../GlobalUsings.cs | 1 + .../Models/VirtualFileSystemTests.cs | 20 ++++++-- .../ValueObjects/VFSDirectoryPathTests.cs | 46 ++++++++--------- .../ValueObjects/VFSFilePathTests.cs | 28 +++------- .../ValueObjects/VFSPathTest.cs | 49 +++++++++--------- 16 files changed, 269 insertions(+), 91 deletions(-) create mode 100644 docs/api/VFSException.VFSException(string).md create mode 100644 docs/api/VFSException.VFSException(string,Exception).md create mode 100644 docs/api/VFSException.md create mode 100644 src/Atypical.VirtualFileSystem.Core/Atypical.VirtualFileSystem.Core.csproj.DotSettings create mode 100644 src/Atypical.VirtualFileSystem.Core/Exceptions/VFSException.cs diff --git a/docs/api/VFSException.VFSException(string).md b/docs/api/VFSException.VFSException(string).md new file mode 100644 index 0000000..8ffd435 --- /dev/null +++ b/docs/api/VFSException.VFSException(string).md @@ -0,0 +1,17 @@ +#### [Atypical.VirtualFileSystem.Core](VirtualFileSystem.md 'VirtualFileSystem') +### [Atypical.VirtualFileSystem.Core.Exceptions](VirtualFileSystem.md#Atypical.VirtualFileSystem.Core.Exceptions 'Atypical.VirtualFileSystem.Core.Exceptions').[VFSException](VFSException.md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException') + +## VFSException(string) Constructor + +Initializes a new instance of the [VFSException](VFSException.md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException') class with a message that describes the error. + +```csharp +public VFSException(string message); +``` +#### Parameters + + + +`message` [System.String](https://docs.microsoft.com/en-us/dotnet/api/System.String 'System.String') + +The error message that explains the reason for the exception. \ No newline at end of file diff --git a/docs/api/VFSException.VFSException(string,Exception).md b/docs/api/VFSException.VFSException(string,Exception).md new file mode 100644 index 0000000..a45cc84 --- /dev/null +++ b/docs/api/VFSException.VFSException(string,Exception).md @@ -0,0 +1,25 @@ +#### [Atypical.VirtualFileSystem.Core](VirtualFileSystem.md 'VirtualFileSystem') +### [Atypical.VirtualFileSystem.Core.Exceptions](VirtualFileSystem.md#Atypical.VirtualFileSystem.Core.Exceptions 'Atypical.VirtualFileSystem.Core.Exceptions').[VFSException](VFSException.md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException') + +## VFSException(string, Exception) Constructor + +Initializes a new instance of the [VFSException](VFSException.md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException') class with a message and an inner exception that is the cause +of this exception. + +```csharp +public VFSException(string message, System.Exception innerException); +``` +#### Parameters + + + +`message` [System.String](https://docs.microsoft.com/en-us/dotnet/api/System.String 'System.String') + +The error message that explains the reason for the exception. + + + +`innerException` [System.Exception](https://docs.microsoft.com/en-us/dotnet/api/System.Exception 'System.Exception') + +The exception that is the cause of the current exception, or a null reference if no inner +exception is specified. \ No newline at end of file diff --git a/docs/api/VFSException.md b/docs/api/VFSException.md new file mode 100644 index 0000000..09670dc --- /dev/null +++ b/docs/api/VFSException.md @@ -0,0 +1,17 @@ +#### [Atypical.VirtualFileSystem.Core](VirtualFileSystem.md 'VirtualFileSystem') +### [Atypical.VirtualFileSystem.Core.Exceptions](VirtualFileSystem.md#Atypical.VirtualFileSystem.Core.Exceptions 'Atypical.VirtualFileSystem.Core.Exceptions') + +## VFSException Class + +Exception thrown by the VFS. + +```csharp +public class VFSException : System.Exception +``` + +Inheritance [System.Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object 'System.Object') 🡒 [System.Exception](https://docs.microsoft.com/en-us/dotnet/api/System.Exception 'System.Exception') 🡒 VFSException + +| Constructors | | +| :--- | :--- | +| [VFSException(string, Exception)](VFSException.VFSException(string,Exception).md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException.VFSException(string, System.Exception)') | Initializes a new instance of the [VFSException](VFSException.md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException') class with a message and an inner exception that is the cause
of this exception. | +| [VFSException(string)](VFSException.VFSException(string).md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException.VFSException(string)') | Initializes a new instance of the [VFSException](VFSException.md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException') class with a message that describes the error. | diff --git a/docs/api/VirtualFileSystem.md b/docs/api/VirtualFileSystem.md index 623bb40..04bdc71 100644 --- a/docs/api/VirtualFileSystem.md +++ b/docs/api/VirtualFileSystem.md @@ -200,6 +200,14 @@ For example, the path of the node with the path "./temp/file.txt" is "./temp/file.txt". The path of the node with the path "./temp/" is "./temp/". + + +## Atypical.VirtualFileSystem.Core.Exceptions Namespace +- **[VFSException](VFSException.md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException')** `Class` Exception thrown by the VFS. + - **[VFSException(string, Exception)](VFSException.VFSException(string,Exception).md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException.VFSException(string, System.Exception)')** `Constructor` Initializes a new instance of the [VFSException](VFSException.md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException') class with a message and an inner exception that is the cause + of this exception. + - **[VFSException(string)](VFSException.VFSException(string).md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException.VFSException(string)')** `Constructor` Initializes a new instance of the [VFSException](VFSException.md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException') class with a message that describes the error. + ## Atypical.VirtualFileSystem.Core.Models Namespace diff --git a/docs/links b/docs/links index e3102d8..5bb7e7a 100644 --- a/docs/links +++ b/docs/links @@ -71,6 +71,10 @@ T:Atypical.VirtualFileSystem.Core.Models.FileNode|FileNode.md|FileNode M:Atypical.VirtualFileSystem.Core.Models.RootNode.#ctor|RootNode.RootNode().md|RootNode() M:Atypical.VirtualFileSystem.Core.Models.RootNode.ToString|RootNode.ToString().md|ToString() T:Atypical.VirtualFileSystem.Core.Models.RootNode|RootNode.md|RootNode +M:Atypical.VirtualFileSystem.Core.Exceptions.VFSException.#ctor(System.String)|VFSException.VFSException(string).md|VFSException(string) +M:Atypical.VirtualFileSystem.Core.Exceptions.VFSException.#ctor(System.String,System.Exception)|VFSException.VFSException(string,Exception).md|VFSException(string, Exception) +N:Atypical.VirtualFileSystem.Core.Exceptions|VirtualFileSystem.md#Atypical.VirtualFileSystem.Core.Exceptions|Atypical.VirtualFileSystem.Core.Exceptions +T:Atypical.VirtualFileSystem.Core.Exceptions.VFSException|VFSException.md|VFSException N:Atypical.VirtualFileSystem.Core.Contracts|VirtualFileSystem.md#Atypical.VirtualFileSystem.Core.Contracts|Atypical.VirtualFileSystem.Core.Contracts T:Atypical.VirtualFileSystem.Core.Contracts.IDirectoryNode|IDirectoryNode.md|IDirectoryNode P:Atypical.VirtualFileSystem.Core.Contracts.IFileNode.Content|IFileNode.Content.md|Content diff --git a/src/Atypical.VirtualFileSystem.Core/Abstractions/VFSPath.cs b/src/Atypical.VirtualFileSystem.Core/Abstractions/VFSPath.cs index ceddade..d442312 100644 --- a/src/Atypical.VirtualFileSystem.Core/Abstractions/VFSPath.cs +++ b/src/Atypical.VirtualFileSystem.Core/Abstractions/VFSPath.cs @@ -22,6 +22,8 @@ public abstract record VFSPath /// public static readonly Regex VFSPathRegex = new(VFSPathRegexPattern, RegexOptions.Compiled); + + /// /// Creates a new instance of . /// @@ -30,7 +32,9 @@ public abstract record VFSPath /// Thrown when the path is invalid. public VFSPath(string path) { - ArgumentNullException.ThrowIfNull(path); + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + if (path is null) + ThrowArgumentHasInvalidFormat(string.Empty); var vfsPath = CleanVFSPathInput(path); @@ -41,11 +45,11 @@ public VFSPath(string path) return; } - if (!VFSPathRegex.IsMatch(vfsPath)) - throw new ArgumentException($"The path '{path}' is invalid.", nameof(path)); - if (vfsPath.Contains($".{DIRECTORY_SEPARATOR}") || vfsPath.Contains($"{DIRECTORY_SEPARATOR}.")) - throw new ArgumentException($"The path '{path}' contains relative path segments.", nameof(path)); + ThrowArgumentHasRelativePathSegment(vfsPath); + + if (!VFSPathRegex.IsMatch(vfsPath)) + ThrowArgumentHasInvalidFormat(vfsPath); Value = vfsPath; @@ -143,6 +147,9 @@ private string CleanVFSPathInput(string path) // if is root path, return it if (cleanPath is ROOT_PATH or "") return cleanPath; + + // replace backslashes with forward slashes + cleanPath = cleanPath.Replace('\\', '/'); // clean up the path - remove leading and trailing slashes cleanPath = cleanPath.TrimStart('/'); @@ -167,8 +174,7 @@ private string CleanVFSPathInput(string path) public VFSPath GetAbsoluteParentPath(int depthFromRoot) { if (depthFromRoot < 0) - throw new ArgumentOutOfRangeException(nameof(depthFromRoot), - "The depth from root must be greater than or equal to 0."); + ThrowDepthFromRootMustBeGreaterThanOrEqualToZero(depthFromRoot); if (IsRoot) return this; @@ -184,4 +190,33 @@ public VFSPath GetAbsoluteParentPath(int depthFromRoot) /// /// A hash code for the current object. public override int GetHashCode() => Value.GetHashCode(); -} \ No newline at end of file + + [DoesNotReturn] + private static void ThrowArgumentHasRelativePathSegment(string vfsPath) + { + var message = $"The path '{vfsPath}' contains a relative path segment."; + throw new VFSException(message, new ArgumentException(message)); + } + + [DoesNotReturn] + private static void ThrowArgumentHasInvalidFormat(string vfsPath) + { + var message = vfsPath.Length > 0 + ? $"The path '{vfsPath}' is invalid." + : "An empty path is invalid."; + + throw new VFSException(message, new ArgumentException(message)); + } + + [DoesNotReturn] + private static void ThrowDepthFromRootMustBeGreaterThanOrEqualToZero(int depthFromRoot) + { + var message = $""" + The depth from root must be greater than or equal to 0. + Actual value: {depthFromRoot}. + """; + + throw new VFSException(message); + } +} + diff --git a/src/Atypical.VirtualFileSystem.Core/Atypical.VirtualFileSystem.Core.csproj.DotSettings b/src/Atypical.VirtualFileSystem.Core/Atypical.VirtualFileSystem.Core.csproj.DotSettings new file mode 100644 index 0000000..61bfa61 --- /dev/null +++ b/src/Atypical.VirtualFileSystem.Core/Atypical.VirtualFileSystem.Core.csproj.DotSettings @@ -0,0 +1,3 @@ + + True + True \ No newline at end of file diff --git a/src/Atypical.VirtualFileSystem.Core/Exceptions/VFSException.cs b/src/Atypical.VirtualFileSystem.Core/Exceptions/VFSException.cs new file mode 100644 index 0000000..2a8cc71 --- /dev/null +++ b/src/Atypical.VirtualFileSystem.Core/Exceptions/VFSException.cs @@ -0,0 +1,36 @@ +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace Atypical.VirtualFileSystem.Core.Exceptions; + +/// +/// Exception thrown by the VFS. +/// +public class VFSException : Exception +{ + /// + /// Initializes a new instance of the class with a message that describes the error. + /// + /// The error message that explains the reason for the exception. + public VFSException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class with a message and an inner exception that is the cause + /// of this exception. + /// + /// The error message that explains the reason for the exception. + /// + /// The exception that is the cause of the current exception, or a null reference if no inner + /// exception is specified. + /// + public VFSException(string message, Exception innerException) + : base(message, innerException) + { + } +} \ No newline at end of file diff --git a/src/Atypical.VirtualFileSystem.Core/GlobalUsings.cs b/src/Atypical.VirtualFileSystem.Core/GlobalUsings.cs index 9bce44d..35077d9 100644 --- a/src/Atypical.VirtualFileSystem.Core/GlobalUsings.cs +++ b/src/Atypical.VirtualFileSystem.Core/GlobalUsings.cs @@ -12,6 +12,7 @@ global using System.Text.RegularExpressions; global using Atypical.VirtualFileSystem.Core.Abstractions; global using Atypical.VirtualFileSystem.Core.Contracts; +global using Atypical.VirtualFileSystem.Core.Exceptions; global using Atypical.VirtualFileSystem.Core.Models; global using Atypical.VirtualFileSystem.Core.ValueObjects; global using static Atypical.VirtualFileSystem.Core.VFSConstants; \ No newline at end of file diff --git a/src/Atypical.VirtualFileSystem.Core/VFS.cs b/src/Atypical.VirtualFileSystem.Core/VFS.cs index a4edb98..d715bb4 100644 --- a/src/Atypical.VirtualFileSystem.Core/VFS.cs +++ b/src/Atypical.VirtualFileSystem.Core/VFS.cs @@ -39,7 +39,7 @@ private void AddToIndex(IVirtualFileSystemNode node) var added = Index.TryAdd(node.Path.Value, node); if (!added) - throw new InvalidOperationException($"The node '{node.Path}' already exists in the index."); + ThrowVirtualNodeAlreadyExists(node); if (node.Path.Parent is not null && !Index.ContainsKey(node.Path.Parent.Value)) CreateDirectory(node.Path.Parent); @@ -135,7 +135,7 @@ public bool TryGetDirectory(string path, out IDirectoryNode? directory) public IVirtualFileSystem CreateDirectory(VFSDirectoryPath directoryPath) { if (directoryPath.IsRoot) - throw new ArgumentException("Cannot create root directory.", nameof(directoryPath)); + ThrowCannotCreateRootDirectory(); var directory = new DirectoryNode(directoryPath); AddToIndex(directory); @@ -150,12 +150,12 @@ public IVirtualFileSystem CreateDirectory(string path) public IVirtualFileSystem DeleteDirectory(VFSDirectoryPath directoryPath) { if (directoryPath.IsRoot) - throw new ArgumentException("Cannot delete root directory.", nameof(directoryPath)); + ThrowCannotDeleteRootDirectory(); // try to get the directory var found = TryGetDirectory(directoryPath, out _); if (!found) - throw new KeyNotFoundException($"The directory '{directoryPath}' does not exist."); + ThrowVirtualDirectoryNotFound(directoryPath); // find the path and its children in the index var paths = Index.Keys @@ -231,7 +231,7 @@ public IVirtualFileSystem DeleteFile(VFSFilePath filePath) // try to get the file var found = TryGetFile(filePath, out _); if (!found) - throw new KeyNotFoundException($"The file '{filePath}' does not exist."); + ThrowVirtualFileNotFound(filePath); // remove the file from the index Index.Remove(filePath.Value); @@ -252,4 +252,39 @@ public IEnumerable FindFiles(Regex regexPattern) => FindFiles().Where(f => regexPattern.IsMatch(f.Path.Value)); #endregion + + [DoesNotReturn] + private static void ThrowVirtualNodeAlreadyExists(IVirtualFileSystemNode node) + { + var message = $"The node '{node.Path}' already exists in the index."; + throw new VFSException(message); + } + + [DoesNotReturn] + private static void ThrowVirtualFileNotFound(VFSFilePath filePath) + { + var message = $"The file '{filePath}' does not exist in the index."; + throw new VFSException(message); + } + + [DoesNotReturn] + private static void ThrowVirtualDirectoryNotFound(VFSDirectoryPath directoryPath) + { + var message = $"The directory '{directoryPath}' does not exist in the index."; + throw new VFSException(message); + } + + [DoesNotReturn] + private static void ThrowCannotDeleteRootDirectory() + { + const string message = "Cannot delete the root directory."; + throw new VFSException(message); + } + + [DoesNotReturn] + private static void ThrowCannotCreateRootDirectory() + { + const string message = "Cannot create the root directory."; + throw new VFSException(message); + } } diff --git a/src/Atypical.VirtualFileSystem.Core/ValueObjects/VFSDirectoryPath.cs b/src/Atypical.VirtualFileSystem.Core/ValueObjects/VFSDirectoryPath.cs index 7ea7790..b5abfd7 100644 --- a/src/Atypical.VirtualFileSystem.Core/ValueObjects/VFSDirectoryPath.cs +++ b/src/Atypical.VirtualFileSystem.Core/ValueObjects/VFSDirectoryPath.cs @@ -24,7 +24,7 @@ public VFSDirectoryPath(string path) // cannot ends with a file extension var lastSegment = Value.Split('/').Last(); if (lastSegment.Contains('.')) - throw new ArgumentException("The path must not contain a file extension.", nameof(path)); + ThrowArgumentHasFileExtension(path); } /// @@ -41,4 +41,11 @@ public VFSDirectoryPath(string path) /// The path to convert. /// The string representation of the path. public static implicit operator string(VFSDirectoryPath path) => path.Value; + + [DoesNotReturn] + private static void ThrowArgumentHasFileExtension(string path) + { + var message = $"The directory path '{path}' contains a file extension."; + throw new VFSException(message, new ArgumentException(message)); + } } \ No newline at end of file diff --git a/tests/Atypical.VirtualFileSystem.UnitTests/GlobalUsings.cs b/tests/Atypical.VirtualFileSystem.UnitTests/GlobalUsings.cs index 05b11b8..c34f193 100644 --- a/tests/Atypical.VirtualFileSystem.UnitTests/GlobalUsings.cs +++ b/tests/Atypical.VirtualFileSystem.UnitTests/GlobalUsings.cs @@ -7,6 +7,7 @@ // Global using directives global using Atypical.VirtualFileSystem.Core; +global using Atypical.VirtualFileSystem.Core.Exceptions; global using Atypical.VirtualFileSystem.Core.Models; global using Atypical.VirtualFileSystem.Core.ValueObjects; global using FluentAssertions; diff --git a/tests/Atypical.VirtualFileSystem.UnitTests/Models/VirtualFileSystemTests.cs b/tests/Atypical.VirtualFileSystem.UnitTests/Models/VirtualFileSystemTests.cs index 9fe4060..ef6fa88 100644 --- a/tests/Atypical.VirtualFileSystem.UnitTests/Models/VirtualFileSystemTests.cs +++ b/tests/Atypical.VirtualFileSystem.UnitTests/Models/VirtualFileSystemTests.cs @@ -164,7 +164,9 @@ public void CreateDirectory_throws_an_exception_if_the_directory_already_exists( Action action = () => vfs.CreateDirectory(directoryPath); // Assert - action.Should().Throw(); + action.Should() + .Throw() + .WithMessage($"The node 'vfs://dir1' already exists in the index."); } [Fact] @@ -179,7 +181,9 @@ public void CreateDirectory_throws_an_exception_if_the_path_is_not_a_directory() Action action = () => vfs.CreateDirectory(filePath); // Assert - action.Should().Throw(); + action.Should() + .Throw() + .WithMessage("The directory path 'vfs://dir1/dir2/dir3/file.txt' contains a file extension."); } } @@ -226,7 +230,9 @@ public void DeleteDirectory_throws_an_exception_if_the_directory_does_not_exist( Action action = () => vfs.DeleteDirectory(directoryPath); // Assert - action.Should().Throw(); + action.Should() + .Throw() + .WithMessage("The directory 'vfs://dir1' does not exist in the index."); } } @@ -368,7 +374,9 @@ public void CreateFile_throws_an_exception_if_the_file_already_exists() Action action = () => vfs.CreateFile(filePath); // Assert - action.Should().Throw(); + action.Should() + .Throw() + .WithMessage("The node 'vfs://dir1/dir2/dir3/file.txt' already exists in the index."); } } @@ -399,7 +407,9 @@ public void DeleteFile_throws_an_exception_if_the_file_does_not_exist() Action action = () => vfs.DeleteFile("dir1/dir2/dir3/file.txt"); // Assert - action.Should().Throw(); + action.Should() + .Throw() + .WithMessage("The file 'vfs://dir1/dir2/dir3/file.txt' does not exist in the index."); } } diff --git a/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSDirectoryPathTests.cs b/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSDirectoryPathTests.cs index 039f913..39804b1 100644 --- a/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSDirectoryPathTests.cs +++ b/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSDirectoryPathTests.cs @@ -17,7 +17,7 @@ public class Constructor // - The path must not contain any consecutive slashes (//). [Fact] - public void Constructor_throw_ArgumentNullException_when_path_is_null() + public void Constructor_throw_VFSException_when_path_is_null() { // Arrange const string path = null!; @@ -29,11 +29,13 @@ public void Constructor_throw_ArgumentNullException_when_path_is_null() }; // Assert - action.Should().Throw(); + action.Should() + .Throw() + .WithMessage("An empty path is invalid."); } [Fact] - public void Constructor_throw_ArgumentException_when_path_is_empty() + public void Constructor_throw_VFSException_when_path_is_empty() { // Arrange const string path = ""; @@ -45,27 +47,13 @@ public void Constructor_throw_ArgumentException_when_path_is_empty() }; // Assert - action.Should().Throw(); + action.Should() + .Throw() + .WithMessage("An empty path is invalid."); } [Fact] - public void Constructor_throw_ArgumentException_when_path_contains_invalid_characters() - { - // Arrange - const string path = @"invalid\path"; - - // Act - var action = () => - { - var _ = new VFSDirectoryPath(path); - }; - - // Assert - action.Should().Throw(); - } - - [Fact] - public void Constructor_throw_ArgumentException_when_path_contains_relative_path_segments() + public void Constructor_throw_VFSException_when_path_contains_relative_path_segments() { // Arrange const string path = @"invalid\..\path"; @@ -77,11 +65,13 @@ public void Constructor_throw_ArgumentException_when_path_contains_relative_path }; // Assert - action.Should().Throw(); + action.Should() + .Throw() + .WithMessage("The path 'vfs://invalid/../path' contains a relative path segment."); } [Fact] - public void Constructor_throw_ArgumentException_when_path_contains_consecutive_slashes() + public void Constructor_throw_VFSException_when_path_contains_consecutive_slashes() { // Arrange const string path = @"invalid//path"; @@ -93,11 +83,13 @@ public void Constructor_throw_ArgumentException_when_path_contains_consecutive_s }; // Assert - action.Should().Throw(); + action.Should() + .Throw() + .WithMessage("The path 'vfs://invalid//path' is invalid."); } [Fact] - public void Constructor_throw_ArgumentException_when_path_is_not_a_directory_path() + public void Constructor_throw_VFSException_when_path_is_not_a_directory_path() { // Arrange const string path = @"invalid/path.txt"; @@ -109,7 +101,9 @@ public void Constructor_throw_ArgumentException_when_path_is_not_a_directory_pat }; // Assert - action.Should().Throw(); + action.Should() + .Throw() + .WithMessage("The directory path 'invalid/path.txt' contains a file extension."); } [Fact] diff --git a/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSFilePathTests.cs b/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSFilePathTests.cs index fbeefab..e1e3ea6 100644 --- a/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSFilePathTests.cs +++ b/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSFilePathTests.cs @@ -18,7 +18,7 @@ public class Constructor // - The path must not end with a slash (/). [Fact] - public void Constructor_throw_ArgumentNullException_when_path_is_null() + public void Constructor_throw_VFSException_when_path_is_null() { // Arrange const string path = null!; @@ -30,11 +30,13 @@ public void Constructor_throw_ArgumentNullException_when_path_is_null() }; // Assert - action.Should().Throw(); + action.Should() + .Throw() + .WithMessage("An empty path is invalid."); } [Fact] - public void Constructor_throw_ArgumentException_when_path_is_empty() + public void Constructor_throw_VFSException_when_path_is_empty() { // Arrange const string path = ""; @@ -46,23 +48,9 @@ public void Constructor_throw_ArgumentException_when_path_is_empty() }; // Assert - action.Should().Throw(); - } - - [Fact] - public void Constructor_throw_ArgumentException_when_path_contains_invalid_characters() - { - // Arrange - const string path = @"invalid\path"; - - // Act - var action = () => - { - var _ = new VFSFilePath(path); - }; - - // Assert - action.Should().Throw(); + action.Should() + .Throw() + .WithMessage("An empty path is invalid."); } } diff --git a/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSPathTest.cs b/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSPathTest.cs index 5ba751a..8d6fefe 100644 --- a/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSPathTest.cs +++ b/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSPathTest.cs @@ -18,7 +18,7 @@ public class Constructor // - The path must not end with a slash (/). [Fact] - public void Constructor_throw_ArgumentNullException_when_path_is_null() + public void Constructor_throw_VFSException_when_path_is_null() { // Arrange const string path = null!; @@ -30,11 +30,13 @@ public void Constructor_throw_ArgumentNullException_when_path_is_null() }; // Assert - action.Should().Throw(); + action.Should() + .Throw() + .WithMessage("An empty path is invalid."); } [Fact] - public void Constructor_throw_ArgumentException_when_path_is_empty() + public void Constructor_throw_VFSException_when_path_is_empty() { // Arrange const string path = ""; @@ -46,27 +48,13 @@ public void Constructor_throw_ArgumentException_when_path_is_empty() }; // Assert - action.Should().Throw(); + action.Should() + .Throw() + .WithMessage("An empty path is invalid."); } [Fact] - public void Constructor_throw_ArgumentException_when_path_contains_invalid_characters() - { - // Arrange - const string path = @"invalid\path"; - - // Act - var action = () => - { - var _ = new VFSDirectoryPath(path); - }; - - // Assert - action.Should().Throw(); - } - - [Fact] - public void Constructor_throw_ArgumentException_when_path_contains_relative_path_segments() + public void Constructor_throw_VFSException_when_path_contains_relative_path_segments() { // Arrange const string path = @"invalid/../path"; @@ -78,11 +66,13 @@ public void Constructor_throw_ArgumentException_when_path_contains_relative_path }; // Assert - action.Should().Throw(); + action.Should() + .Throw() + .WithMessage("The path 'vfs://invalid/../path' contains a relative path segment."); } [Fact] - public void Constructor_throw_ArgumentException_when_path_contains_consecutive_slashes() + public void Constructor_throw_VFSException_when_path_contains_consecutive_slashes() { // Arrange const string path = @"invalid//path"; @@ -94,7 +84,9 @@ public void Constructor_throw_ArgumentException_when_path_contains_consecutive_s }; // Assert - action.Should().Throw(); + action.Should() + .Throw() + .WithMessage("The path 'vfs://invalid//path' is invalid."); } [Fact] @@ -337,7 +329,7 @@ public void MethodGetAbsoluteParentPath_return_root_when_path_is_root() } [Fact] - public void MethodGetAbsoluteParentPath_throw_exception_when_depth_is_negative() + public void MethodGetAbsoluteParentPath_throw_VFSException_when_depth_is_negative() { // Arrange const string path = @"directory/subdirectory"; @@ -347,7 +339,12 @@ public void MethodGetAbsoluteParentPath_throw_exception_when_depth_is_negative() Action action = () => vfsPath.GetAbsoluteParentPath(-1); // Assert - action.Should().Throw(); + action.Should() + .Throw() + .WithMessage($""" + The depth from root must be greater than or equal to 0. + Actual value: {-1}. + """); } [Fact]