using System.Text.Json;
namespace GisConverter.TestsApp.ConsoleApp
{
///
/// Tests that validate output file correctness, not just existence.
/// Ensures converted files are valid and contain expected data.
///
///
/// - Integration-style tests that invoke the ConsoleApp entry point via Program.Run(string[]).
/// - These tests exercise conversion pipeline end-to-end using small, synthetic inputs created in a unique
/// temporary root for each test class instance to avoid interference with other tests.
/// - Console output streams (Console.Out and Console.Error) are captured so assertions can inspect
/// user-facing diagnostics without launching a new process.
/// - Tests assume Program.Run returns an integer exit code and does not call Environment.Exit.
/// - Tests are defensive:
/// - They fail fast when the conversion returns a non-zero exit code and include captured stdout/stderr to aid diagnosis.
/// - When asserting content, prefer tolerant checks (presence of key tokens / well-formed JSON) rather than exact text.
/// - Clean-up: temporary folders are removed in using best-effort deletion to avoid masking test failures.
/// - Notes for contributors:
/// - Keep sample inputs small to keep tests fast.
/// - If converters require external resources or licenses (e.g., Aspose), gate such tests or skip when resources are not available.
///
[Collection("Logging")]
public class OutputValidationTests : 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 temporary root.
///
///
/// - Redirects console streams to in-memory buffers so tests can assert on CLI output.
/// - Creates a unique temporary directory per test class instance under the system temp folder.
/// - The temporary root is used to store synthetic input files, output folders and temp folders for conversions.
///
public OutputValidationTests()
{
_originalOut = Console.Out;
_originalErr = Console.Error;
_outWriter = new StringWriter();
_errWriter = new StringWriter();
Console.SetOut(_outWriter);
Console.SetError(_errWriter);
_tempRoot = Path.Combine(Path.GetTempPath(), "GisConverter.ValidationTests", Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(_tempRoot);
}
///
/// Tear-down — restore console streams and remove temporary artifacts.
///
///
/// - Always restore original console streams to avoid affecting other tests.
/// - Dispose the in-memory writers and attempt to delete the temporary root (best-effort).
/// - Avoid throwing from cleanup to prevent masking the original test failure.
///
public void Dispose()
{
Console.SetOut(_originalOut);
Console.SetError(_originalErr);
_outWriter.Dispose();
_errWriter.Dispose();
try { if (Directory.Exists(_tempRoot)) Directory.Delete(_tempRoot, true); } catch { }
}
///
/// Verify CSV -> GeoJSON conversion produces valid GeoJSON files.
///
///
/// - Arranges: writes a small CSV with longitude/latitude and names.
/// - Act: invokes Program.Run requesting GeoJson target.
/// - Assert:
/// - Conversion exit code must be 0; otherwise the test fails and prints captured output for diagnosis.
/// - At least one .json output file is produced.
/// - Each produced JSON file is parsed with to verify it is syntactically valid JSON
/// and contains a top-level type property whose value is either FeatureCollection or Feature.
/// - Failure modes:
/// - Invalid JSON will cause the catch block to fail the test with the filename and parse error.
///
[Fact(DisplayName = "CSV to GeoJSON produces valid JSON")]
public void Run_CsvToGeoJson_ProducesValidJson()
{
var csvInput = Path.Combine(_tempRoot, "points.csv");
File.WriteAllText(csvInput, "lon,lat,name\n-122.4,37.8,\"San Francisco\"\n-118.2,34.0,\"Los Angeles\"");
var outDir = Path.Combine(_tempRoot, "out");
var tempDir = Path.Combine(_tempRoot, "temp");
var args = new[]
{
"gis_convert",
csvInput,
"GeoJson",
outDir,
tempDir,
"unused"
};
var exit = GisConverter.ConsoleApp.Program.Run(args);
if (exit != 0)
{
var output = _outWriter.ToString() + _errWriter.ToString();
Assert.True(false, $"Conversion failed: {output}");
}
var outputFiles = Directory.GetFiles(outDir, "*.json", SearchOption.AllDirectories);
Assert.NotEmpty(outputFiles);
foreach (var file in outputFiles)
{
var json = File.ReadAllText(file);
try
{
var doc = JsonDocument.Parse(json);
Assert.True(doc.RootElement.TryGetProperty("type", out var typeElement));
var type = typeElement.GetString();
Assert.True(type == "FeatureCollection" || type == "Feature",
$"Expected GeoJSON type, got {type}");
}
catch (JsonException ex)
{
Assert.True(false, $"Output file {file} is not valid JSON: {ex.Message}");
}
}
}
///
/// Ensures that conversion produces non-empty output files.
///
///
/// - Arranges a small CSV file, runs a conversion that should produce at least one output file.
/// - Asserts that the output directory contains files and that each output file's length is greater than zero bytes.
/// - If conversion fails (non-zero exit), the test will not attempt file checks.
/// - This test verifies that converters do not create empty placeholders on success.
///
[Fact(DisplayName = "Output files are not empty")]
public void Run_Conversion_OutputFilesNotEmpty()
{
var csvInput = Path.Combine(_tempRoot, "data.csv");
File.WriteAllText(csvInput, "a,b,c\n1,2,3\n4,5,6");
var outDir = Path.Combine(_tempRoot, "out");
var tempDir = Path.Combine(_tempRoot, "temp");
var args = new[]
{
"gis_convert",
csvInput,
"Csv",
outDir,
tempDir,
"unused"
};
var exit = GisConverter.ConsoleApp.Program.Run(args);
if (exit == 0)
{
var files = Directory.GetFiles(outDir, "*", SearchOption.AllDirectories);
Assert.NotEmpty(files);
foreach (var file in files)
{
var info = new FileInfo(file);
Assert.True(info.Length > 0, $"Output file {file} is empty");
}
}
}
///
/// Verifies that unicode filenames are accepted and do not produce encoding or invalid path errors.
///
///
/// - Creates an input file with a Unicode filename and runs a conversion.
/// - Asserts that console output does not contain common error tokens such as "encoding" or "invalid".
/// - This test focuses on CLI/OS-level handling of Unicode paths rather than content correctness.
/// - Note: some filesystems or CI runners may behave differently with certain Unicode characters; skip or adapt tests if the environment cannot support the characters used.
///
[Fact(DisplayName = "Unicode filenames are handled correctly")]
public void Run_UnicodeFilename_HandledCorrectly()
{
var unicodeFilename = "שכבות_מידע.csv";
var csvInput = Path.Combine(_tempRoot, unicodeFilename);
File.WriteAllText(csvInput, "x,y\n1,2\n3,4");
var outDir = Path.Combine(_tempRoot, "out");
var tempDir = Path.Combine(_tempRoot, "temp");
var args = new[]
{
"gis_convert",
csvInput,
"Csv",
outDir,
tempDir,
"unused"
};
_outWriter.GetStringBuilder().Clear();
_errWriter.GetStringBuilder().Clear();
var exit = GisConverter.ConsoleApp.Program.Run(args);
var output = _outWriter.ToString() + _errWriter.ToString();
Assert.DoesNotContain("encoding", output.ToLowerInvariant());
Assert.DoesNotContain("invalid", output.ToLowerInvariant());
}
///
/// Ensures that converted CSV output preserves column headers and basic data tokens.
///
///
/// - Writes a small CSV input with known headers and values, runs conversion to CSV (round-trip or transformation scenario).
/// - Asserts that output CSV files contain the expected header tokens so that key columns are preserved.
/// - If multiple CSV outputs exist, the test checks each for the presence of header tokens.
/// - This test does not assert exact row ordering or full equality — only that essential headers and tokens exist.
///
[Fact(DisplayName = "Conversion preserves data integrity")]
public void Run_CsvConversion_PreservesData()
{
var csvInput = Path.Combine(_tempRoot, "preserve.csv");
var expectedData = "id,value\n1,test\n2,data";
File.WriteAllText(csvInput, expectedData);
var outDir = Path.Combine(_tempRoot, "out");
var tempDir = Path.Combine(_tempRoot, "temp");
var args = new[]
{
"gis_convert",
csvInput,
"Csv",
outDir,
tempDir,
"unused"
};
var exit = GisConverter.ConsoleApp.Program.Run(args);
if (exit == 0)
{
var outputFiles = Directory.GetFiles(outDir, "*.csv", SearchOption.AllDirectories);
Assert.NotEmpty(outputFiles);
foreach (var file in outputFiles)
{
var content = File.ReadAllText(file);
Assert.Contains("id", content);
Assert.Contains("value", content);
}
}
}
}
}