diff --git a/test/DemaConsulting.BuildMark.Tests/PathHelpersTests.cs b/test/DemaConsulting.BuildMark.Tests/PathHelpersTests.cs new file mode 100644 index 0000000..8983134 --- /dev/null +++ b/test/DemaConsulting.BuildMark.Tests/PathHelpersTests.cs @@ -0,0 +1,101 @@ +// Copyright (c) DEMA Consulting +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace DemaConsulting.BuildMark.Tests; + +/// +/// Tests for the PathHelpers class. +/// +[TestClass] +public class PathHelpersTests +{ + /// + /// Test that SafePathCombine correctly combines valid paths. + /// + [TestMethod] + public void PathHelpers_SafePathCombine_ValidPaths_CombinesCorrectly() + { + // Arrange + var basePath = "/home/user/project"; + var relativePath = "subfolder/file.txt"; + + // Act + var result = PathHelpers.SafePathCombine(basePath, relativePath); + + // Assert + Assert.AreEqual(Path.Combine(basePath, relativePath), result); + } + + /// + /// Test that SafePathCombine throws ArgumentException for path traversal with double dots. + /// + [TestMethod] + public void PathHelpers_SafePathCombine_PathTraversalWithDoubleDots_ThrowsArgumentException() + { + // Arrange + var basePath = "/home/user/project"; + var relativePath = "../etc/passwd"; + + // Act & Assert + var exception = Assert.Throws(() => + PathHelpers.SafePathCombine(basePath, relativePath)); + Assert.Contains("Invalid path component", exception.Message); + } + + /// + /// Test that SafePathCombine throws ArgumentException for path with double dots in middle. + /// + [TestMethod] + public void PathHelpers_SafePathCombine_DoubleDotsInMiddle_ThrowsArgumentException() + { + // Arrange + var basePath = "/home/user/project"; + var relativePath = "subfolder/../../../etc/passwd"; + + // Act & Assert + var exception = Assert.Throws(() => + PathHelpers.SafePathCombine(basePath, relativePath)); + Assert.Contains("Invalid path component", exception.Message); + } + + /// + /// Test that SafePathCombine throws ArgumentException for absolute paths. + /// + [TestMethod] + public void PathHelpers_SafePathCombine_AbsolutePath_ThrowsArgumentException() + { + // Test Unix absolute path + var unixBasePath = "/home/user/project"; + var unixRelativePath = "/etc/passwd"; + var unixException = Assert.Throws(() => + PathHelpers.SafePathCombine(unixBasePath, unixRelativePath)); + Assert.Contains("Invalid path component", unixException.Message); + + // Test Windows absolute path (only on Windows since Windows paths may not be rooted on Unix) + if (OperatingSystem.IsWindows()) + { + var windowsBasePath = "C:\\Users\\project"; + var windowsRelativePath = "C:\\Windows\\System32\\file.txt"; + var windowsException = Assert.Throws(() => + PathHelpers.SafePathCombine(windowsBasePath, windowsRelativePath)); + Assert.Contains("Invalid path component", windowsException.Message); + } + } +} diff --git a/test/DemaConsulting.BuildMark.Tests/ValidationTests.cs b/test/DemaConsulting.BuildMark.Tests/ValidationTests.cs new file mode 100644 index 0000000..43b937c --- /dev/null +++ b/test/DemaConsulting.BuildMark.Tests/ValidationTests.cs @@ -0,0 +1,236 @@ +// Copyright (c) DEMA Consulting +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using DemaConsulting.BuildMark.RepoConnectors; + +namespace DemaConsulting.BuildMark.Tests; + +/// +/// Tests for the Validation class. +/// +[TestClass] +public class ValidationTests +{ + /// + /// Test that Validation.Run writes TRX results file when specified. + /// + [TestMethod] + public void Validation_Run_WithTrxResultsFile_WritesTrxFile() + { + // Arrange + var tempDir = Path.Combine(Path.GetTempPath(), $"buildmark_test_{Guid.NewGuid()}"); + Directory.CreateDirectory(tempDir); + + try + { + var trxFile = Path.Combine(tempDir, "results.trx"); + var args = new[] { "--validate", "--results", trxFile }; + + StringWriter? outputWriter = null; + StringWriter? errorWriter = null; + + try + { + // Capture console output + outputWriter = new StringWriter(); + errorWriter = new StringWriter(); + Console.SetOut(outputWriter); + Console.SetError(errorWriter); + + // Act + using var context = Context.Create(args, () => new MockRepoConnector()); + Validation.Run(context); + + // Assert - Verify TRX file was created + Assert.IsTrue(File.Exists(trxFile), "TRX file should be created"); + + // Verify TRX file contains expected content + var trxContent = File.ReadAllText(trxFile); + Assert.Contains("TestRun", trxContent); + Assert.Contains("BuildMark Self-Validation", trxContent); + } + finally + { + // Restore console output + var standardOutput = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true }; + Console.SetOut(standardOutput); + var standardError = new StreamWriter(Console.OpenStandardError()) { AutoFlush = true }; + Console.SetError(standardError); + + outputWriter?.Dispose(); + errorWriter?.Dispose(); + } + } + finally + { + // Cleanup + if (Directory.Exists(tempDir)) + { + Directory.Delete(tempDir, true); + } + } + } + + /// + /// Test that Validation.Run writes JUnit XML results file when specified. + /// + [TestMethod] + public void Validation_Run_WithXmlResultsFile_WritesJUnitFile() + { + // Arrange + var tempDir = Path.Combine(Path.GetTempPath(), $"buildmark_test_{Guid.NewGuid()}"); + Directory.CreateDirectory(tempDir); + + try + { + var xmlFile = Path.Combine(tempDir, "results.xml"); + var args = new[] { "--validate", "--results", xmlFile }; + + StringWriter? outputWriter = null; + StringWriter? errorWriter = null; + + try + { + // Capture console output + outputWriter = new StringWriter(); + errorWriter = new StringWriter(); + Console.SetOut(outputWriter); + Console.SetError(errorWriter); + + // Act + using var context = Context.Create(args, () => new MockRepoConnector()); + Validation.Run(context); + + // Assert - Verify XML file was created + Assert.IsTrue(File.Exists(xmlFile), "XML file should be created"); + + // Verify XML file contains expected content + var xmlContent = File.ReadAllText(xmlFile); + Assert.Contains("testsuites", xmlContent); + Assert.Contains("BuildMark Self-Validation", xmlContent); + } + finally + { + // Restore console output + var standardOutput = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true }; + Console.SetOut(standardOutput); + var standardError = new StreamWriter(Console.OpenStandardError()) { AutoFlush = true }; + Console.SetError(standardError); + + outputWriter?.Dispose(); + errorWriter?.Dispose(); + } + } + finally + { + // Cleanup + if (Directory.Exists(tempDir)) + { + Directory.Delete(tempDir, true); + } + } + } + + /// + /// Test that Validation.Run handles unsupported results file extension. + /// + [TestMethod] + public void Validation_Run_WithUnsupportedResultsFileExtension_ShowsError() + { + // Arrange + var tempDir = Path.Combine(Path.GetTempPath(), $"buildmark_test_{Guid.NewGuid()}"); + Directory.CreateDirectory(tempDir); + + try + { + var unsupportedFile = Path.Combine(tempDir, "results.json"); + var args = new[] { "--validate", "--results", unsupportedFile }; + + StringWriter? outputWriter = null; + + try + { + // Capture console output + outputWriter = new StringWriter(); + Console.SetOut(outputWriter); + + // Act + using var context = Context.Create(args, () => new MockRepoConnector()); + Validation.Run(context); + + // Assert - Verify error message in output (WriteError writes to Console.WriteLine) + var output = outputWriter.ToString(); + Assert.Contains("Unsupported results file format", output); + } + finally + { + // Restore console output + var standardOutput = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true }; + Console.SetOut(standardOutput); + + outputWriter?.Dispose(); + } + } + finally + { + // Cleanup + if (Directory.Exists(tempDir)) + { + Directory.Delete(tempDir, true); + } + } + } + + /// + /// Test that Validation.Run handles write failure for results file. + /// + [TestMethod] + public void Validation_Run_WithInvalidResultsFilePath_ShowsError() + { + // Arrange + var invalidPath = Path.Combine("/invalid_path_that_does_not_exist_12345678", "results.trx"); + var args = new[] { "--validate", "--results", invalidPath }; + + StringWriter? outputWriter = null; + + try + { + // Capture console output + outputWriter = new StringWriter(); + Console.SetOut(outputWriter); + + // Act + using var context = Context.Create(args, () => new MockRepoConnector()); + Validation.Run(context); + + // Assert - Verify error message in output (WriteError writes to Console.WriteLine) + var output = outputWriter.ToString(); + Assert.Contains("Failed to write results file", output); + } + finally + { + // Restore console output + var standardOutput = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true }; + Console.SetOut(standardOutput); + + outputWriter?.Dispose(); + } + } +}