namespace GisConverter.TestsApp.ConsoleApp
{
///
/// Comprehensive exit code validation tests.
/// Verifies all four exit code paths defined in Program.ExitCode enum.
///
///
/// - These tests exercise the ConsoleApp entry point via the public Program.Run(string[]) method and
/// assert on returned integer exit codes and captured console output.
/// - The test class is marked with [Collection("Logging")] to serialize execution with other logging-related
/// tests and avoid races over global logger state or shared log files.
/// - Each test captures Console.Out and Console.Error to in-memory buffers so assertions can inspect
/// user-facing diagnostics without spawning a separate process.
/// - Tests create isolated temporary folders for inputs/outputs to avoid interference and perform best-effort cleanup
/// in .
/// - Assumptions:
/// - Program.Run returns an exit code and does not call Environment.Exit.
/// - Messaging (usage text, warnings) may change slightly; prefer using substring, case-insensitive assertions.
///
[Collection("Logging")]
public class ExitCodeTests : IDisposable
{
private readonly TextWriter _originalOut;
private readonly TextWriter _originalErr;
private readonly StringWriter _outWriter;
private readonly StringWriter _errWriter;
private readonly string _tempRoot;
///
/// Test constructor — capture console streams and create an isolated temp root for files used by tests.
///
///
/// - Redirects Console.Out and Console.Error to StringWriters so tests can assert on output.
/// - Creates a unique temp folder per test class instance; tests should remove it in .
///
public ExitCodeTests()
{
_originalOut = Console.Out;
_originalErr = Console.Error;
_outWriter = new StringWriter();
_errWriter = new StringWriter();
Console.SetOut(_outWriter);
Console.SetError(_errWriter);
_tempRoot = Path.Combine(Path.GetTempPath(), "GisConverter.ExitCodeTests", Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(_tempRoot);
}
///
/// Tear-down — restore console streams and remove temporary artifacts.
///
///
/// - Always restore the original console streams to avoid affecting other tests.
/// - Performs best-effort deletion 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 a successful conversion returns exit code 0.
///
///
/// - Arranges a minimal valid CSV input and expects the converter stub to succeed.
/// - If the CLI does not return 0 the test fails and prints captured output for diagnosis.
///
[Fact(DisplayName = "Successful conversion returns exit code 0")]
public void Run_SuccessfulConversion_ReturnsZero()
{
var dummyInput = Path.Combine(_tempRoot, "success.csv");
File.WriteAllText(dummyInput, "x,y\n1,2\n3,4");
var outDir = Path.Combine(_tempRoot, "out");
var tempDir = Path.Combine(_tempRoot, "temp");
var args = new[]
{
"gis_convert",
dummyInput,
"Csv",
outDir,
tempDir,
"unused"
};
var exit = GisConverter.ConsoleApp.Program.Run(args);
if (exit == 0)
{
Assert.Equal(0, exit);
}
else
{
var output = _outWriter.ToString() + _errWriter.ToString();
Assert.True(false, $"Expected exit 0 for valid CSV conversion but got {exit}. Output: {output}");
}
}
///
/// Verifies missing input file produces a non-zero exit code.
///
///
/// - The CLI may return 1 (app error) or 2 (conversion failed) depending on the code path; the test accepts either.
/// - Use this test to ensure the application does not silently succeed when inputs are missing.
///
[Fact(DisplayName = "Missing input file returns non-zero exit code")]
public void Run_MissingInputFile_ReturnsError()
{
var nonexistentInput = Path.Combine(_tempRoot, "doesnotexist.csv");
var outDir = Path.Combine(_tempRoot, "out");
var tempDir = Path.Combine(_tempRoot, "temp");
var args = new[]
{
"gis_convert",
nonexistentInput,
"Csv",
outDir,
tempDir,
"unused"
};
var exit = GisConverter.ConsoleApp.Program.Run(args);
Assert.True(exit == 1 || exit == 2, $"Expected exit 1 or 2 for missing file, got {exit}");
}
///
/// Verifies that an invalid target format results in an application error exit code (1).
///
///
/// - The test uses a dummy CSV input and supplies an unrecognized format option.
/// - The expected behavior is that the CLI reports the invalid option and returns exit code 1.
///
[Fact(DisplayName = "Invalid target format returns app error (1)")]
public void Run_InvalidTargetFormat_ReturnsAppError()
{
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,
"InvalidFormatThatDoesNotExist",
outDir,
tempDir,
"unused"
};
var exit = GisConverter.ConsoleApp.Program.Run(args);
Assert.Equal(1, exit);
}
///
/// Verifies help flags return the application error exit code (1).
///
///
/// - Validates that top-level help options and the formats help option produce consistent exit codes and show help text.
///
[Fact(DisplayName = "Help commands return app error (1)")]
public void Run_HelpCommands_ReturnAppError()
{
var exitHelp = GisConverter.ConsoleApp.Program.Run(new[] { "help" });
var exitDashHelp = GisConverter.ConsoleApp.Program.Run(new[] { "--help" });
var exitFormats = GisConverter.ConsoleApp.Program.Run(new[] { "--help-formats" });
Assert.Equal(1, exitHelp);
Assert.Equal(1, exitDashHelp);
Assert.Equal(1, exitFormats);
}
///
/// Verifies insufficient arguments returns app error (1).
///
///
/// - Ensures the CLI validates minimum argument count and returns the appropriate application error code.
///
[Fact(DisplayName = "Insufficient arguments returns app error (1)")]
public void Run_InsufficientArgs_ReturnsAppError()
{
var args = new[] { "gis_convert" };
var exit = GisConverter.ConsoleApp.Program.Run(args);
Assert.Equal(1, exit);
}
}
}