From 7cf2b0433a7dc52dc7e37ef3f9a93b298e19e660 Mon Sep 17 00:00:00 2001 From: Bruno Massa Date: Thu, 25 Apr 2024 01:18:18 -0500 Subject: [PATCH 1/4] chore: maintain only file and directory into IFileSystem --- source/Commands/NewSiteCommand.cs | 6 +-- source/Helpers/IFileSystem.cs | 75 ++++++++++++++++++++++++++----- 2 files changed, 66 insertions(+), 15 deletions(-) diff --git a/source/Commands/NewSiteCommand.cs b/source/Commands/NewSiteCommand.cs index 3b9ae66..1f2c73a 100644 --- a/source/Commands/NewSiteCommand.cs +++ b/source/Commands/NewSiteCommand.cs @@ -40,8 +40,8 @@ public sealed partial class NewSiteCommand(NewSiteOptions options, ILogger logge /// public int Run() { - var outputPath = fileSystem.GetFullPath(options.Output); - var siteSettingsPath = fileSystem.Combine(outputPath, "sucos.yaml"); + var outputPath = Path.GetFullPath(options.Output); + var siteSettingsPath = Path.Combine(outputPath, "sucos.yaml"); if (fileSystem.FileExists(siteSettingsPath) && !options.Force) { @@ -75,7 +75,7 @@ public sealed partial class NewSiteCommand(NewSiteOptions options, ILogger logge foreach (var folder in folders) { logger.Information("Creating {folder}", folder); - fileSystem.CreateDirectory(folder); + fileSystem.DirectoryCreateDirectory(folder); } } } \ No newline at end of file diff --git a/source/Helpers/IFileSystem.cs b/source/Helpers/IFileSystem.cs index b6e5eeb..ad5e97c 100644 --- a/source/Helpers/IFileSystem.cs +++ b/source/Helpers/IFileSystem.cs @@ -6,19 +6,32 @@ namespace SuCoS; public interface IFileSystem { /// - /// Path.GetFullPath + /// Directory.CreateDirectory + /// + /// + void DirectoryCreateDirectory(string path); + + /// + /// Directory.Exists /// /// /// - string GetFullPath(string path); + bool DirectoryExists(string path); /// - /// Path.Combine + /// Directory.GetFiles /// - /// - /// + /// /// - string Combine(string path1, string path2); + string[] DirectoryGetFiles(string path); + + /// + /// Directory.GetFiles + /// + /// + /// + /// + string[] DirectoryGetFiles(string path, string searchPattern); /// /// File.Exists @@ -28,10 +41,28 @@ public interface IFileSystem bool FileExists(string path); /// - /// Directory.CreateDirectory + /// File.Copy + /// + /// + /// + /// + /// + void FileCopy(string sourceFileName, string destFileName, bool overwrite); + + /// + /// File.WriteAllText + /// + /// + /// + /// + void FileWriteAllText(string path, string? contents); + + /// + /// File.ReadAllText /// /// - void CreateDirectory(string path); + /// + string FileReadAllText(string path); } /// @@ -40,14 +71,34 @@ public interface IFileSystem public class FileSystem : IFileSystem { /// - public string GetFullPath(string path) => Path.GetFullPath(path); + public void DirectoryCreateDirectory(string path) + => Directory.CreateDirectory(path); + + /// + public bool DirectoryExists(string path) + => Directory.Exists(path); + + /// + public string[] DirectoryGetFiles(string path) + => Directory.GetFiles(path); + + /// + public string[] DirectoryGetFiles(string path, string searchPattern) + => Directory.GetFiles(path, searchPattern); + + /// + public bool FileExists(string path) + => File.Exists(path); /// - public string Combine(string path1, string path2) => Path.Combine(path1, path2); + public void FileCopy(string sourceFileName, string destFileName, bool overwrite) + => File.Copy(sourceFileName, destFileName, overwrite); /// - public bool FileExists(string path) => File.Exists(path); + public void FileWriteAllText(string path, string? contents) + => File.WriteAllText(path, contents); /// - public void CreateDirectory(string path) => Directory.CreateDirectory(path); + public string FileReadAllText(string path) + => File.ReadAllText(path); } -- GitLab From bc014539f4f8e7f467d07061ddd3f17fd7a43c5d Mon Sep 17 00:00:00 2001 From: Bruno Massa Date: Thu, 25 Apr 2024 01:22:42 -0500 Subject: [PATCH 2/4] chore: maintain only file and directory into IFileSystem --- test/Commands/NewSiteCommandTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/Commands/NewSiteCommandTests.cs b/test/Commands/NewSiteCommandTests.cs index af1c371..a14823a 100644 --- a/test/Commands/NewSiteCommandTests.cs +++ b/test/Commands/NewSiteCommandTests.cs @@ -84,8 +84,8 @@ public class NewSiteCommandTests newSiteCommand.Run(); // Assert - fileSystem.Received(1).CreateDirectory("folder1"); - fileSystem.Received(1).CreateDirectory("folder2"); + fileSystem.Received(1).DirectoryCreateDirectory("folder1"); + fileSystem.Received(1).DirectoryCreateDirectory("folder2"); } [Fact] @@ -96,7 +96,7 @@ public class NewSiteCommandTests var site = Substitute.For(); site.SourceFolders.Returns(["folder1", "folder2"]); fileSystem.FileExists(Arg.Any()).Returns(false); - fileSystem.When(x => x.CreateDirectory(Arg.Any())) + fileSystem.When(x => x.DirectoryCreateDirectory(Arg.Any())) .Do(x => { throw new ArgumentNullException(); }); var newSiteCommand = new NewSiteCommand(options, logger, fileSystem, site); @@ -189,6 +189,6 @@ public class NewSiteCommandTests newSiteCommand.Run(); // Assert - fileSystem.Received(2).CreateDirectory(Arg.Any()); + fileSystem.Received(2).DirectoryCreateDirectory(Arg.Any()); } } -- GitLab From f32b68ee8306374e0b910604cb669e9dfe6cd2ce Mon Sep 17 00:00:00 2001 From: Bruno Massa Date: Thu, 25 Apr 2024 01:33:54 -0500 Subject: [PATCH 3/4] refactor: implement more and more IFileSystem --- source/Commands/BaseGeneratorCommand.cs | 11 ++++++++-- source/Commands/BuildCommand.cs | 21 ++++++++++--------- source/Commands/ServeCommand.cs | 6 ++++-- source/Helpers/SiteHelper.cs | 15 +++++++------ source/Program.cs | 4 ++-- test/Commands/BaseGeneratorCommandTests.cs | 16 ++++++++++---- test/Models/SiteTests.cs | 14 ++++++++++--- .../RegisteredPageRequestHandlerTests.cs | 5 ++++- 8 files changed, 62 insertions(+), 30 deletions(-) diff --git a/source/Commands/BaseGeneratorCommand.cs b/source/Commands/BaseGeneratorCommand.cs index 6eba9d3..0c4a8e8 100644 --- a/source/Commands/BaseGeneratorCommand.cs +++ b/source/Commands/BaseGeneratorCommand.cs @@ -36,20 +36,27 @@ public abstract class BaseGeneratorCommand /// protected ILogger logger { get; } + /// + /// File system functions (file and directory) + /// + protected readonly IFileSystem fs; + /// /// Initializes a new instance of the class. /// /// The generate options. /// The logger instance. Injectable for testing - protected BaseGeneratorCommand(IGenerateOptions options, ILogger logger) + /// + protected BaseGeneratorCommand(IGenerateOptions options, ILogger logger, IFileSystem fs) { ArgumentNullException.ThrowIfNull(options); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); stopwatch = new(logger); + this.fs = fs; logger.Information("Source path: {source}", propertyValue: options.Source); - site = SiteHelper.Init(configFile, options, Parser, logger, stopwatch); + site = SiteHelper.Init(configFile, options, Parser, logger, stopwatch, fs); } } diff --git a/source/Commands/BuildCommand.cs b/source/Commands/BuildCommand.cs index e023d28..b67dd17 100644 --- a/source/Commands/BuildCommand.cs +++ b/source/Commands/BuildCommand.cs @@ -10,14 +10,15 @@ namespace SuCoS; public class BuildCommand : BaseGeneratorCommand { private readonly BuildOptions options; - /// /// Entry point of the build command. It will be called by the main program /// in case the build command is invoked (which is by default). /// /// Command line options /// The logger instance. Injectable for testing - public BuildCommand(BuildOptions options, ILogger logger) : base(options, logger) + /// + public BuildCommand(BuildOptions options, ILogger logger, IFileSystem fs) + : base(options, logger, fs) { this.options = options ?? throw new ArgumentNullException(nameof(options)); @@ -57,11 +58,11 @@ public class BuildCommand : BaseGeneratorCommand var outputAbsolutePath = Path.Combine(options.Output, path); var outputDirectory = Path.GetDirectoryName(outputAbsolutePath); - _ = Directory.CreateDirectory(outputDirectory!); + fs.DirectoryCreateDirectory(outputDirectory!); // Save the processed output to the final file var result = page.CompleteContent; - File.WriteAllText(outputAbsolutePath, result); + fs.FileWriteAllText(outputAbsolutePath, result); // Log logger.Debug("Page created: {Permalink}", outputAbsolutePath); @@ -74,10 +75,10 @@ public class BuildCommand : BaseGeneratorCommand var outputAbsolutePath = Path.Combine(options.Output, resource.Permalink!.TrimStart('/')); var outputDirectory = Path.GetDirectoryName(outputAbsolutePath); - _ = Directory.CreateDirectory(outputDirectory!); + fs.DirectoryCreateDirectory(outputDirectory!); // Copy the file to the output folder - File.Copy(resource.SourceFullPath, outputAbsolutePath, overwrite: true); + fs.FileCopy(resource.SourceFullPath, outputAbsolutePath, overwrite: true); } }); @@ -90,7 +91,7 @@ public class BuildCommand : BaseGeneratorCommand /// /// The source folder to copy from. /// The output folder to copy to. - private static void CopyFolder(string source, string output) + private void CopyFolder(string source, string output) { // Check if the source folder even exists if (!Directory.Exists(source)) @@ -99,10 +100,10 @@ public class BuildCommand : BaseGeneratorCommand } // Create the output folder if it doesn't exist - _ = Directory.CreateDirectory(output); + fs.DirectoryCreateDirectory(output); // Get all files in the source folder - var files = Directory.GetFiles(source); + var files = fs.DirectoryGetFiles(source); foreach (var fileFullPath in files) { @@ -113,7 +114,7 @@ public class BuildCommand : BaseGeneratorCommand var destinationFullPath = Path.Combine(output, fileName); // Copy the file to the output folder - File.Copy(fileFullPath, destinationFullPath, overwrite: true); + fs.FileCopy(fileFullPath, destinationFullPath, overwrite: true); } } } diff --git a/source/Commands/ServeCommand.cs b/source/Commands/ServeCommand.cs index fa6d742..17fa492 100644 --- a/source/Commands/ServeCommand.cs +++ b/source/Commands/ServeCommand.cs @@ -60,7 +60,9 @@ public sealed class ServeCommand : BaseGeneratorCommand, IDisposable /// ServeOptions object specifying the serve options. /// The logger instance. Injectable for testing /// - public ServeCommand(ServeOptions options, ILogger logger, IFileWatcher fileWatcher) : base(options, logger) + /// + public ServeCommand(ServeOptions options, ILogger logger, IFileWatcher fileWatcher, IFileSystem fs) + : base(options, logger, fs) { this.options = options ?? throw new ArgumentNullException(nameof(options)); this.fileWatcher = fileWatcher ?? throw new ArgumentNullException(nameof(fileWatcher)); @@ -161,7 +163,7 @@ public sealed class ServeCommand : BaseGeneratorCommand, IDisposable } // Reinitialize the site - site = SiteHelper.Init(configFile, options, Parser, logger, stopwatch); + site = SiteHelper.Init(configFile, options, Parser, logger, stopwatch, fs); StartServer(baseURLDefault, portDefault); }).ConfigureAwait(false); diff --git a/source/Helpers/SiteHelper.cs b/source/Helpers/SiteHelper.cs index 5f0bcdd..2c043c1 100644 --- a/source/Helpers/SiteHelper.cs +++ b/source/Helpers/SiteHelper.cs @@ -23,14 +23,15 @@ public static class SiteHelper /// Creates the pages dictionary. /// /// - public static Site Init(string configFile, IGenerateOptions options, IMetadataParser parser, ILogger logger, StopwatchReporter stopwatch) + public static Site Init(string configFile, IGenerateOptions options, IMetadataParser parser, ILogger logger, StopwatchReporter stopwatch, IFileSystem fs) { ArgumentNullException.ThrowIfNull(stopwatch); + ArgumentNullException.ThrowIfNull(fs); SiteSettings siteSettings; try { - siteSettings = ParseSettings(configFile, options, parser); + siteSettings = ParseSettings(configFile, options, parser, fs); } catch { @@ -47,7 +48,7 @@ public static class SiteHelper stopwatch.Stop("Parse", site.FilesParsedToReport); - if (Directory.Exists(Path.GetFullPath(site.SourceThemePath))) + if (fs.DirectoryExists(Path.GetFullPath(site.SourceThemePath))) { site.TemplateEngine.Initialize(site); } @@ -87,21 +88,23 @@ public static class SiteHelper /// /// The generate options. /// The front matter parser. + /// /// The site settings file. /// The site settings. - public static SiteSettings ParseSettings(string configFile, IGenerateOptions options, IMetadataParser parser) + public static SiteSettings ParseSettings(string configFile, IGenerateOptions options, IMetadataParser parser, IFileSystem fs) { ArgumentNullException.ThrowIfNull(options); ArgumentNullException.ThrowIfNull(parser); + ArgumentNullException.ThrowIfNull(fs); // Read the main configation var filePath = Path.Combine(options.Source, configFile); - if (!File.Exists(filePath)) + if (!fs.FileExists(filePath)) { throw new FileNotFoundException($"The {configFile} file was not found in the specified source directory: {options.Source}"); } - var fileContent = File.ReadAllText(filePath); + var fileContent = fs.FileReadAllText(filePath); var siteSettings = parser.Parse(fileContent) ?? throw new FormatException($"Error reading app config {configFile}"); return siteSettings; diff --git a/source/Program.cs b/source/Program.cs index c6cf3f0..8ef44d5 100644 --- a/source/Program.cs +++ b/source/Program.cs @@ -64,7 +64,7 @@ public class Program(ILogger logger) { try { - _ = new BuildCommand(options, logger); + _ = new BuildCommand(options, logger, new FileSystem()); } catch (Exception ex) { @@ -77,7 +77,7 @@ public class Program(ILogger logger) { try { - var serveCommand = new ServeCommand(options, logger, new SourceFileWatcher()); + var serveCommand = new ServeCommand(options, logger, new SourceFileWatcher(), new FileSystem()); serveCommand.StartServer(); await Task.Delay(-1).ConfigureAwait(false); // Wait forever. } diff --git a/test/Commands/BaseGeneratorCommandTests.cs b/test/Commands/BaseGeneratorCommandTests.cs index 28040d2..ecfd6d2 100644 --- a/test/Commands/BaseGeneratorCommandTests.cs +++ b/test/Commands/BaseGeneratorCommandTests.cs @@ -1,3 +1,4 @@ +using NSubstitute; using Serilog; using SuCoS; using SuCoS.Models.CommandLineOptions; @@ -16,19 +17,26 @@ public class BaseGeneratorCommandTests private static readonly ILogger testLogger = new LoggerConfiguration().CreateLogger(); - private class BaseGeneratorCommandStub(IGenerateOptions options, ILogger logger) - : BaseGeneratorCommand(options, logger); + private class BaseGeneratorCommandStub(IGenerateOptions options, ILogger logger, IFileSystem fs) + : BaseGeneratorCommand(options, logger, fs); + + readonly IFileSystem fs; + + public BaseGeneratorCommandTests() + { + fs = Substitute.For(); + } [Fact] public void Constructor_ShouldThrowArgumentNullException_WhenOptionsIsNull() { - _ = Assert.Throws(() => new BaseGeneratorCommandStub(null!, testLogger)); + _ = Assert.Throws(() => new BaseGeneratorCommandStub(null!, testLogger, fs)); } [Fact] public void Constructor_ShouldThrowArgumentNullException_WhenLoggerIsNull() { - _ = Assert.Throws(() => new BaseGeneratorCommandStub(testOptions, null!)); + _ = Assert.Throws(() => new BaseGeneratorCommandStub(testOptions, null!, fs)); } [Fact] diff --git a/test/Models/SiteTests.cs b/test/Models/SiteTests.cs index 9207cb4..0dda74d 100644 --- a/test/Models/SiteTests.cs +++ b/test/Models/SiteTests.cs @@ -1,3 +1,4 @@ +using SuCoS; using SuCoS.Helpers; using SuCoS.Models; using SuCoS.Models.CommandLineOptions; @@ -10,6 +11,13 @@ namespace Tests.Models; /// public class SiteTests : TestSetup { + readonly IFileSystem fs; + + public SiteTests() + { + fs = new FileSystem(); + } + [Theory] [InlineData("test01.md")] [InlineData("date-ok.md")] @@ -234,7 +242,7 @@ public class SiteTests : TestSetup SourceArgument = Path.GetFullPath(Path.Combine(testSitesPath, testSitePathCONST05)) }; var parser = new SuCoS.Parser.YAMLParser(); - var siteSettings = SiteHelper.ParseSettings("sucos.yaml", options, parser); + var siteSettings = SiteHelper.ParseSettings("sucos.yaml", options, parser, fs); site = new Site(options, siteSettings, parser, loggerMock, null); // Act @@ -262,7 +270,7 @@ public class SiteTests : TestSetup SourceArgument = Path.GetFullPath(Path.Combine(testSitesPath, testSitePathCONST07)) }; var parser = new SuCoS.Parser.YAMLParser(); - var siteSettings = SiteHelper.ParseSettings("sucos.yaml", options, parser); + var siteSettings = SiteHelper.ParseSettings("sucos.yaml", options, parser, fs); site = new Site(options, siteSettings, parser, loggerMock, null); // Act @@ -304,7 +312,7 @@ public class SiteTests : TestSetup SourceArgument = Path.GetFullPath(Path.Combine(testSitesPath, testSitePathCONST06)) }; var parser = new SuCoS.Parser.YAMLParser(); - var siteSettings = SiteHelper.ParseSettings("sucos.yaml", options, parser); + var siteSettings = SiteHelper.ParseSettings("sucos.yaml", options, parser, fs); site = new Site(options, siteSettings, parser, loggerMock, null); // Act diff --git a/test/ServerHandlers/RegisteredPageRequestHandlerTests.cs b/test/ServerHandlers/RegisteredPageRequestHandlerTests.cs index a0125e8..6794463 100644 --- a/test/ServerHandlers/RegisteredPageRequestHandlerTests.cs +++ b/test/ServerHandlers/RegisteredPageRequestHandlerTests.cs @@ -1,4 +1,5 @@ using NSubstitute; +using SuCoS; using SuCoS.Helpers; using SuCoS.Models; using SuCoS.Models.CommandLineOptions; @@ -41,7 +42,9 @@ public class RegisteredPageRequestHandlerTests : TestSetup SourceArgument = siteFullPath }; var parser = new SuCoS.Parser.YAMLParser(); - var siteSettings = SiteHelper.ParseSettings("sucos.yaml", options, parser); + // FIXME: make it an argument + var fs = new FileSystem(); + var siteSettings = SiteHelper.ParseSettings("sucos.yaml", options, parser, fs); site = new Site(options, siteSettings, parser, loggerMock, null); var registeredPageRequest = new RegisteredPageRequest(site); -- GitLab From 6742aa26781e75223bceaa5cb9186adf38c84850 Mon Sep 17 00:00:00 2001 From: Bruno Massa Date: Thu, 25 Apr 2024 02:19:11 -0500 Subject: [PATCH 4/4] test: implement basic test suite for Build command --- source/Commands/BuildCommand.cs | 14 +- source/Helpers/IFileSystem.cs | 11 ++ source/Helpers/SiteHelper.cs | 2 +- source/Models/ISite.cs | 3 +- source/Models/Site.cs | 20 +-- source/Program.cs | 4 +- test/Commands/BuildCommandTests.cs | 121 ++++++++++++++++++ test/Models/SiteTests.cs | 26 ++-- .../RegisteredPageRequestHandlerTests.cs | 11 +- 9 files changed, 177 insertions(+), 35 deletions(-) create mode 100644 test/Commands/BuildCommandTests.cs diff --git a/source/Commands/BuildCommand.cs b/source/Commands/BuildCommand.cs index b67dd17..92983d6 100644 --- a/source/Commands/BuildCommand.cs +++ b/source/Commands/BuildCommand.cs @@ -17,11 +17,17 @@ public class BuildCommand : BaseGeneratorCommand /// Command line options /// The logger instance. Injectable for testing /// - public BuildCommand(BuildOptions options, ILogger logger, IFileSystem fs) + public BuildCommand(BuildOptions options, ILogger logger, IFileSystem fs) : base(options, logger, fs) { this.options = options ?? throw new ArgumentNullException(nameof(options)); + } + /// + /// Run the commmand + /// + public int Run() + { logger.Information("Output path: {output}", options.Output); // Generate the site pages @@ -38,6 +44,8 @@ public class BuildCommand : BaseGeneratorCommand // Generate the build report stopwatch.LogReport(site.Title); + + return 0; } private void CreateOutputFiles() @@ -91,10 +99,10 @@ public class BuildCommand : BaseGeneratorCommand /// /// The source folder to copy from. /// The output folder to copy to. - private void CopyFolder(string source, string output) + public void CopyFolder(string source, string output) { // Check if the source folder even exists - if (!Directory.Exists(source)) + if (!fs.DirectoryExists(source)) { return; } diff --git a/source/Helpers/IFileSystem.cs b/source/Helpers/IFileSystem.cs index ad5e97c..1100c90 100644 --- a/source/Helpers/IFileSystem.cs +++ b/source/Helpers/IFileSystem.cs @@ -33,6 +33,13 @@ public interface IFileSystem /// string[] DirectoryGetFiles(string path, string searchPattern); + /// + /// Directory.GetDirectories + /// + /// + /// + string[] DirectoryGetDirectories(string path); + /// /// File.Exists /// @@ -86,6 +93,10 @@ public class FileSystem : IFileSystem public string[] DirectoryGetFiles(string path, string searchPattern) => Directory.GetFiles(path, searchPattern); + /// + public string[] DirectoryGetDirectories(string path) + => Directory.GetDirectories(path); + /// public bool FileExists(string path) => File.Exists(path); diff --git a/source/Helpers/SiteHelper.cs b/source/Helpers/SiteHelper.cs index 2c043c1..39e8873 100644 --- a/source/Helpers/SiteHelper.cs +++ b/source/Helpers/SiteHelper.cs @@ -44,7 +44,7 @@ public static class SiteHelper stopwatch.Start("Parse"); - site.ParseAndScanSourceFiles(site.SourceContentPath); + site.ParseAndScanSourceFiles(fs, site.SourceContentPath); stopwatch.Stop("Parse", site.FilesParsedToReport); diff --git a/source/Models/ISite.cs b/source/Models/ISite.cs index cdbc88b..3c92051 100644 --- a/source/Models/ISite.cs +++ b/source/Models/ISite.cs @@ -86,11 +86,12 @@ public interface ISite : ISiteSettings, IParams /// Search recursively for all markdown files in the content folder, then /// parse their content for front matter meta data and markdown. /// + /// /// Folder to scan /// Folder recursive level /// Page of the upper directory /// - public void ParseAndScanSourceFiles(string? directory, int level = 0, IPage? parent = null); + public void ParseAndScanSourceFiles(IFileSystem fs, string? directory, int level = 0, IPage? parent = null); /// /// Extra calculation and automatic data for each page. diff --git a/source/Models/Site.cs b/source/Models/Site.cs index c315c2b..27c3263 100644 --- a/source/Models/Site.cs +++ b/source/Models/Site.cs @@ -14,7 +14,6 @@ public class Site : ISite { #region IParams - /// /// public Dictionary Params { @@ -187,19 +186,14 @@ public class Site : ISite OutputReferences.Clear(); } - /// - /// Search recursively for all markdown files in the content folder, then - /// parse their content for front matter meta data and markdown. - /// - /// Folder to scan - /// Folder recursive level - /// Page of the upper directory - /// - public void ParseAndScanSourceFiles(string? directory, int level = 0, IPage? parent = null) + /// + public void ParseAndScanSourceFiles(IFileSystem fs, string? directory, int level = 0, IPage? parent = null) { + ArgumentNullException.ThrowIfNull(fs); + directory ??= SourceContentPath; - var markdownFiles = Directory.GetFiles(directory, "*.md"); + var markdownFiles = fs.DirectoryGetFiles(directory, "*.md"); ParseIndexPage(directory, level, ref parent, ref markdownFiles); @@ -208,10 +202,10 @@ public class Site : ISite _ = ParseSourceFile(filePath, parent); }); - var subdirectories = Directory.GetDirectories(directory); + var subdirectories = fs.DirectoryGetDirectories(directory); _ = Parallel.ForEach(subdirectories, subdirectory => { - ParseAndScanSourceFiles(subdirectory, level + 1, parent); + ParseAndScanSourceFiles(fs, subdirectory, level + 1, parent); }); } diff --git a/source/Program.cs b/source/Program.cs index 8ef44d5..b8a7da5 100644 --- a/source/Program.cs +++ b/source/Program.cs @@ -64,14 +64,14 @@ public class Program(ILogger logger) { try { - _ = new BuildCommand(options, logger, new FileSystem()); + var command = new BuildCommand(options, logger, new FileSystem()); + return Task.FromResult(command.Run()); } catch (Exception ex) { logger.Error($"Build failed: {ex.Message}"); return Task.FromResult(1); } - return Task.FromResult(0); }, async (ServeOptions options) => { diff --git a/test/Commands/BuildCommandTests.cs b/test/Commands/BuildCommandTests.cs new file mode 100644 index 0000000..eb1aef0 --- /dev/null +++ b/test/Commands/BuildCommandTests.cs @@ -0,0 +1,121 @@ +using SuCoS.Models.CommandLineOptions; +using Xunit; +using NSubstitute; +using Serilog; +using SuCoS; + +namespace Tests.Commands; + +public class BuildCommandTests +{ + readonly ILogger logger; + readonly IFileSystem fileSystem; + readonly BuildOptions options; + + public BuildCommandTests() + { + logger = Substitute.For(); + fileSystem = Substitute.For(); + fileSystem.FileExists("./sucos.yaml").Returns(true); + fileSystem.FileReadAllText("./sucos.yaml").Returns(""" +Ttile: test +"""); + options = new BuildOptions { Output = "test" }; + } + + [Fact] + public void Constructor_ShouldNotThrowException_WhenParametersAreValid() + { + // Act + var result = new BuildCommand(options, logger, fileSystem); + + // Assert + Assert.IsType(result); + } + + [Fact] + public void Constructor_ShouldThrowArgumentNullException_WhenOptionsIsNull() + { + // Act and Assert + Assert.Throws(() => new BuildCommand(null!, logger, fileSystem)); + } + + [Fact] + public void Run() + { + // Act + var command = new BuildCommand(options, logger, fileSystem); + var result = command.Run(); + + // Assert + Assert.Equal(0, result); + } + + // [Fact] + // public void CreateOutputFiles_ShouldCallFileWriteAllText_WhenPageIsValid() + // { + // // Arrange + // site.OutputReferences.Returns(new Dictionary + // { + // { "test", Substitute.For() } + // }); + // Path.Combine(options.Output, "test").Returns("testPath"); + + // var buildCommand = new BuildCommand(options, logger, fileSystem); + + // // Act + // buildCommand.CreateOutputFiles(); + + // // Assert + // fileSystem.Received(1).FileWriteAllText("testPath", Arg.Any()); + // } + + // [Fact] + // public void CreateOutputFiles_ShouldCallFileCopy_WhenResourceIsValid() + // { + // // Arrange + // var resource = Substitute.For(); + // resource.Permalink.Returns("testPermalink"); + // resource.SourceFullPath.Returns("testSourcePath"); + // site.OutputReferences.Returns(new Dictionary + // { + // { "test", resource } + // }); + // Path.Combine(options.Output, "testPermalink").Returns("testDestinationPath"); + // var buildCommand = new BuildCommand(options, logger, fileSystem); + + // // Act + // buildCommand.CreateOutputFiles(); + + // // Assert + // fileSystem.Received(1).FileCopy("testSourcePath", "testDestinationPath", overwrite: true); + // } + + [Fact] + public void CopyFolder_ShouldCallCreateDirectory_WhenSourceFolderExists() + { + // Arrange + fileSystem.DirectoryExists("sourceFolder").Returns(true); + var buildCommand = new BuildCommand(options, logger, fileSystem); + + // Act + buildCommand.CopyFolder("sourceFolder", "outputFolder"); + + // Assert + fileSystem.Received(1).DirectoryCreateDirectory("outputFolder"); + } + + [Fact] + public void CopyFolder_ShouldNotCallCreateDirectory_WhenSourceFolderDoesNotExist() + { + // Arrange + fileSystem.DirectoryExists("sourceFolder").Returns(false); + var buildCommand = new BuildCommand(options, logger, fileSystem); + + // Act + buildCommand.CopyFolder("sourceFolder", "outputFolder"); + + // Assert + fileSystem.DidNotReceive().DirectoryCreateDirectory(Arg.Any()); + } +} diff --git a/test/Models/SiteTests.cs b/test/Models/SiteTests.cs index 0dda74d..8b6532c 100644 --- a/test/Models/SiteTests.cs +++ b/test/Models/SiteTests.cs @@ -31,7 +31,7 @@ public class SiteTests : TestSetup }; // Act - site.ParseAndScanSourceFiles(Path.Combine(siteFullPath, "content")); + site.ParseAndScanSourceFiles(fs, Path.Combine(siteFullPath, "content")); // Assert Assert.Contains(site.Pages, page => page.SourceRelativePathDirectory!.Length == 0); @@ -50,7 +50,7 @@ public class SiteTests : TestSetup site.Options = options; // Act - site.ParseAndScanSourceFiles(site.SourceContentPath); + site.ParseAndScanSourceFiles(fs, site.SourceContentPath); // Assert Assert.NotNull(site.Home); @@ -71,7 +71,7 @@ public class SiteTests : TestSetup site.Options = options; // Act - site.ParseAndScanSourceFiles(null); + site.ParseAndScanSourceFiles(fs, null); // Assert Assert.Equal(expectedQuantity, site.OutputReferences.Values.Where(output => output is IPage page && page.IsSection).Count()); @@ -91,7 +91,7 @@ public class SiteTests : TestSetup site.Options = options; // Act - site.ParseAndScanSourceFiles(null); + site.ParseAndScanSourceFiles(fs, null); // Assert Assert.Equal(expectedQuantity, site.OutputReferences.Values.Where(output => output is IPage page).Count()); @@ -111,7 +111,7 @@ public class SiteTests : TestSetup site.Options = options; // Act - site.ParseAndScanSourceFiles(null); + site.ParseAndScanSourceFiles(fs, null); // Assert Assert.Equal(expectedQuantity, site.OutputReferences.Values.Where(output => output is IPage page && page.IsPage).Count()); @@ -127,7 +127,7 @@ public class SiteTests : TestSetup site.Options = options; // Act - site.ParseAndScanSourceFiles(null); + site.ParseAndScanSourceFiles(fs, null); // Assert Assert.Equal(100, site.RegularPages.First().Weight); @@ -144,7 +144,7 @@ public class SiteTests : TestSetup site.Options = options; // Act - site.ParseAndScanSourceFiles(null); + site.ParseAndScanSourceFiles(fs, null); // Assert Assert.Equal(0, site.RegularPages.First().Weight); @@ -161,7 +161,7 @@ public class SiteTests : TestSetup site.Options = options; // Act - site.ParseAndScanSourceFiles(null); + site.ParseAndScanSourceFiles(fs, null); // Assert _ = site.OutputReferences.TryGetValue("/tags", out var output); @@ -184,7 +184,7 @@ public class SiteTests : TestSetup site.Options = options; // Act - site.ParseAndScanSourceFiles(null); + site.ParseAndScanSourceFiles(fs, null); // Assert _ = site.OutputReferences.TryGetValue("/tags/tag1", out var output); @@ -209,7 +209,7 @@ public class SiteTests : TestSetup site.Options = options; // Act - site.ParseAndScanSourceFiles(null); + site.ParseAndScanSourceFiles(fs, null); // Assert _ = site.OutputReferences.TryGetValue(url, out var output); @@ -246,7 +246,7 @@ public class SiteTests : TestSetup site = new Site(options, siteSettings, parser, loggerMock, null); // Act - site.ParseAndScanSourceFiles(null); + site.ParseAndScanSourceFiles(fs, null); // Assert _ = site.OutputReferences.TryGetValue(url, out var output); @@ -274,7 +274,7 @@ public class SiteTests : TestSetup site = new Site(options, siteSettings, parser, loggerMock, null); // Act - site.ParseAndScanSourceFiles(null); + site.ParseAndScanSourceFiles(fs, null); // Assert _ = site.OutputReferences.TryGetValue(url, out var output); @@ -316,7 +316,7 @@ public class SiteTests : TestSetup site = new Site(options, siteSettings, parser, loggerMock, null); // Act - site.ParseAndScanSourceFiles(null); + site.ParseAndScanSourceFiles(fs, null); // Assert _ = site.OutputReferences.TryGetValue(url, out var output); diff --git a/test/ServerHandlers/RegisteredPageRequestHandlerTests.cs b/test/ServerHandlers/RegisteredPageRequestHandlerTests.cs index 6794463..33c7653 100644 --- a/test/ServerHandlers/RegisteredPageRequestHandlerTests.cs +++ b/test/ServerHandlers/RegisteredPageRequestHandlerTests.cs @@ -10,6 +10,13 @@ namespace Tests.ServerHandlers; public class RegisteredPageRequestHandlerTests : TestSetup { + readonly IFileSystem fs; + + public RegisteredPageRequestHandlerTests() + { + fs = new FileSystem(); + } + [Theory] [InlineData("/", true)] [InlineData("/testPage", false)] @@ -24,7 +31,7 @@ public class RegisteredPageRequestHandlerTests : TestSetup var registeredPageRequest = new RegisteredPageRequest(site); // Act - site.ParseAndScanSourceFiles(Path.Combine(siteFullPath, "content")); + site.ParseAndScanSourceFiles(fs, Path.Combine(siteFullPath, "content")); // Assert Assert.Equal(exist, registeredPageRequest.Check(requestPath)); @@ -54,7 +61,7 @@ public class RegisteredPageRequestHandlerTests : TestSetup _ = response.OutputStream.Returns(stream); // Act - site.ParseAndScanSourceFiles(Path.Combine(siteFullPath, "content")); + site.ParseAndScanSourceFiles(fs, Path.Combine(siteFullPath, "content")); _ = registeredPageRequest.Check(requestPath); var code = await registeredPageRequest.Handle(response, requestPath, DateTime.Now).ConfigureAwait(true); -- GitLab