namespace GisConverter.TestsApp.ConsoleApp
{
///
/// Tests for logging configuration in Program.cs.
/// Validates that logging initialization handles various edge cases gracefully.
///
///
/// - These are integration-style tests that invoke the ConsoleApp entry point via Program.Run.
/// - Tests capture Console.Out and Console.Error so assertions examine user-facing diagnostics.
/// - Each test creates an isolated temporary root to avoid cross-test interference and performs best-effort cleanup in .
/// - The test collection attribute [Collection("Logging")] serializes execution with other logging tests to avoid races on global logger state and log files.
/// - Tests assume Program.Run returns an exit code and does not call Environment.Exit.
///
[Collection("Logging")]
public class LoggingTests : IDisposable
{
private readonly TextWriter _originalOut;
private readonly TextWriter _originalErr;
private readonly StringWriter _outWriter;
private readonly StringWriter _errWriter;
private readonly string _tempRoot;
///
/// Test ctor — capture console streams and create an isolated temp root.
///
///
/// - Redirects Console.Out and Console.Error to in-memory buffers so tests can assert on output.
/// - Creates a unique temp folder used by tests to store dummy inputs, log files and output folders.
///
public LoggingTests()
{
_originalOut = Console.Out;
_originalErr = Console.Error;
_outWriter = new StringWriter();
_errWriter = new StringWriter();
Console.SetOut(_outWriter);
Console.SetError(_errWriter);
_tempRoot = Path.Combine(Path.GetTempPath(), "GisConverter.LogTests", Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(_tempRoot);
}
///
/// Tear-down — restore console streams and remove temporary artifacts.
///
///
/// - Always restores original console streams to avoid affecting other tests.
/// - Attempts best-effort cleanup of the temporary root and disposes in-memory writers.
///
public void Dispose()
{
Console.SetOut(_originalOut);
Console.SetError(_originalErr);
_outWriter.Dispose();
_errWriter.Dispose();
try { if (Directory.Exists(_tempRoot)) Directory.Delete(_tempRoot, true); } catch { }
}
///
/// Verifies that providing a valid log path and level results in either a created log file or a clear warning when logging fails.
///
///
/// Arrange: create a small dummy input file and a target log path under the temporary root.
/// Act: invoke Program.Run with the Log token, path and level.
/// Assert: either the log file exists or a stderr warning about disabled logging is present.
///
[Fact(DisplayName = "Logging with valid path and level creates log file")]
public void Run_WithValidLogPath_CreatesLogFile()
{
var dummyInput = Path.Combine(_tempRoot, "dummy.csv");
File.WriteAllText(dummyInput, "x,y\n1,2");
var logPath = Path.Combine(_tempRoot, "test.log");
var outDir = Path.Combine(_tempRoot, "out");
var tempDir = Path.Combine(_tempRoot, "temp");
var args = new[]
{
"gis_convert",
dummyInput,
"Csv",
outDir,
tempDir,
"Log",
logPath,
"DEBUG"
};
var exit = GisConverter.ConsoleApp.Program.Run(args);
Assert.True(File.Exists(logPath) || _errWriter.ToString().Contains("warning: logging disabled"),
$"Expected log file at {logPath} or a warning about logging failure");
}
///
/// Verifies that an invalid log path causes a warning to be printed to stderr.
///
///
/// - The test passes an impossible nested path so SetLogger will fail and report a warning.
/// - Asserts that stderr contains the expected "warning: logging disabled" token (case-insensitive).
///
[Fact(DisplayName = "Logging with invalid path prints warning to stderr")]
public void Run_WithInvalidLogPath_PrintsWarningToStderr()
{
var dummyInput = Path.Combine(_tempRoot, "dummy.csv");
File.WriteAllText(dummyInput, "x,y\n1,2");
var invalidLogPath = Path.Combine(_tempRoot, "nonexistent", "subdir", "test.log");
var outDir = Path.Combine(_tempRoot, "out");
var tempDir = Path.Combine(_tempRoot, "temp");
var args = new[]
{
"gis_convert",
dummyInput,
"Csv",
outDir,
tempDir,
"Log",
invalidLogPath
};
_errWriter.GetStringBuilder().Clear();
var exit = GisConverter.ConsoleApp.Program.Run(args);
var stderr = _errWriter.ToString();
Assert.Contains("warning: logging disabled", stderr.ToLowerInvariant());
}
///
/// Ensures that tokens following the required argument count are ignored when the Log keyword is absent.
///
///
/// - Arrange: provide extra unrelated tokens after the required arguments.
/// - Act: run the CLI without the Log token.
/// - Assert: no logging warning is printed (the extra tokens are ignored with respect to logging).
///
[Fact(DisplayName = "Logging without Log keyword ignores subsequent tokens")]
public void Run_WithoutLogKeyword_IgnoresLogPath()
{
var dummyInput = Path.Combine(_tempRoot, "dummy.csv");
File.WriteAllText(dummyInput, "x,y\n1,2");
var outDir = Path.Combine(_tempRoot, "out");
var tempDir = Path.Combine(_tempRoot, "temp");
var args = new[]
{
"gis_convert",
dummyInput,
"Csv",
outDir,
tempDir,
"some_random_token",
"random_path.log"
};
var exit = GisConverter.ConsoleApp.Program.Run(args);
var stderr = _errWriter.ToString();
Assert.DoesNotContain("warning: logging disabled", stderr.ToLowerInvariant());
}
///
/// Verifies that log level parsing accepts case-insensitive tokens.
///
///
/// - Pass a lowercase log level and assert the CLI does not treat it as unknown or error.
/// - This ensures GetOptionalLogArguments and SetLogger accept mixed/case-insensitive level names.
///
[Fact(DisplayName = "Log level parsing is case-insensitive")]
public void Run_LogLevelCaseInsensitive_AcceptsAnyCase()
{
var dummyInput = Path.Combine(_tempRoot, "dummy.csv");
File.WriteAllText(dummyInput, "x,y\n1,2");
var logPath = Path.Combine(_tempRoot, "test_case.log");
var outDir = Path.Combine(_tempRoot, "out");
var tempDir = Path.Combine(_tempRoot, "temp");
var args = new[]
{
"gis_convert",
dummyInput,
"Csv",
outDir,
tempDir,
"Log",
logPath,
"debug"
};
var exit = GisConverter.ConsoleApp.Program.Run(args);
var stderr = _errWriter.ToString();
Assert.DoesNotContain("unknown", stderr.ToLowerInvariant());
}
///
/// Ensures log paths that include spaces are handled correctly by the parser and logger initialization.
///
///
/// - Creates a directory whose name contains spaces and verifies either the log file is created or a warning is printed.
/// - This validates token joining logic and that file paths with spaces are supported when passed as a single token.
///
[Fact(DisplayName = "Log path with spaces is parsed correctly")]
public void Run_LogPathWithSpaces_ParsedCorrectly()
{
var dummyInput = Path.Combine(_tempRoot, "dummy.csv");
File.WriteAllText(dummyInput, "x,y\n1,2");
var logDir = Path.Combine(_tempRoot, "log folder with spaces");
Directory.CreateDirectory(logDir);
var logPath = Path.Combine(logDir, "test.log");
var outDir = Path.Combine(_tempRoot, "out");
var tempDir = Path.Combine(_tempRoot, "temp");
var args = new[]
{
"gis_convert",
dummyInput,
"Csv",
outDir,
tempDir,
"Log",
logPath,
"INFO"
};
var exit = GisConverter.ConsoleApp.Program.Run(args);
Assert.True(File.Exists(logPath) || _errWriter.ToString().Contains("warning"),
"Expected log file to be created when path contains spaces");
}
}
}