using System.IO.Compression;
using GitConverter.Lib.Converters;
using GitConverter.Lib.Models;
namespace GitConverter.TestsApp.Converters
{
///
/// Unit tests for covering early-failure paths and input validation.
///
///
/// These tests exercise the orchestrator logic of UniversalGisConverter while avoiding invocation of
/// heavy third-party conversion code. The strategy is to drive the method into well-defined early
/// failure branches (invalid inputs, unknown format mappings, archive component checks) and assert
/// that appropriate failures are returned.
///
/// The file also contains a set of integration-style tests that are intended to run only when the
/// test runner is configured to perform licensed/integration scenarios. These tests check end-to-end
/// conversions and filesystem outcomes and therefore require a valid runtime (and often an Aspose
/// license). To enable them set the environment variable `GITCONVERTER_ENABLE_INTEGRATION=1` in
/// your local environment or test host. When the environment variable is not set these tests return
/// early so they do not affect CI runs that do not have licensed capabilities.
///
public class UniversalGisConverterTests : IDisposable
{
private readonly string _root;
public UniversalGisConverterTests()
{
_root = Path.Combine(Path.GetTempPath(), "GitConverter.UniversalTests", Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(_root);
}
public void Dispose()
{
try { if (Directory.Exists(_root)) Directory.Delete(_root, true); } catch { }
}
[Fact(DisplayName = "Convert_NonexistentInput_ReturnsFailure")]
public void Convert_NonexistentInput_ReturnsFailure()
{
var conv = new UniversalGisConverter();
var missing = Path.Combine(_root, "does-not-exist.geojson");
var outDir = Path.Combine(_root, "out");
var temp = Path.Combine(_root, "temp");
var res = conv.Convert(missing, "GeoJson", "GeoJson", outDir, temp);
Assert.Equal(ConversionStatus.Failure, res.Status);
Assert.NotEmpty(res.Message);
Assert.Contains("does not exist", res.Message, StringComparison.OrdinalIgnoreCase);
}
[Fact(DisplayName = "Convert_UnknownSourceFormat_ReturnsFailure")]
public void Convert_UnknownSourceFormat_ReturnsFailure()
{
var conv = new UniversalGisConverter();
var input = Path.Combine(_root, "f.txt");
File.WriteAllText(input, "x");
var outDir = Path.Combine(_root, "out");
var temp = Path.Combine(_root, "temp");
var res = conv.Convert(input, "UnknownFormat", "GeoJson", outDir, temp);
Assert.Equal(ConversionStatus.Failure, res.Status);
Assert.NotEmpty(res.Message);
// Accept either mapping-specific or generic invalid format messages to keep the test resilient.
var msg = (res.Message ?? string.Empty).ToLowerInvariant();
Assert.True(
msg.Contains("could not map")
|| msg.Contains("invalid source or destination format")
|| msg.Contains("invalid source")
|| msg.Contains("invalid destination")
|| msg.Contains("unsupported")
|| msg.Contains("unknown"),
$"Unexpected message: {res.Message}");
}
[Fact(DisplayName = "Convert_UnknownTargetDriver_ReturnsFailure")]
public void Convert_UnknownTargetDriver_ReturnsFailure()
{
var conv = new UniversalGisConverter();
// Create a simple geojson input file so PrepareSourceFile succeeds for single-file inputs
var input = Path.Combine(_root, "a.geojson");
File.WriteAllText(input, "{ \"type\": \"FeatureCollection\", \"features\": [] }");
var outDir = Path.Combine(_root, "out");
var temp = Path.Combine(_root, "temp");
var res = conv.Convert(input, "GeoJson", "InvalidTargetOption", outDir, temp);
Assert.Equal(ConversionStatus.Failure, res.Status);
Assert.NotEmpty(res.Message);
Assert.Contains("Invalid source or destination format", res.Message, StringComparison.OrdinalIgnoreCase);
}
[Fact(DisplayName = "Convert_ShapefileArchiveMissingComponents_ReturnsFailure")]
public void Convert_ShapefileArchiveMissingComponents_ReturnsFailure()
{
var conv = new UniversalGisConverter();
// Create a zip that lacks .shp/.shx/.dbf required components
var zipPath = Path.Combine(_root, "incomplete.zip");
var tempDir = Path.Combine(_root, "zsrc");
Directory.CreateDirectory(tempDir);
File.WriteAllText(Path.Combine(tempDir, "only.txt"), "x");
System.IO.Compression.ZipFile.CreateFromDirectory(tempDir, zipPath);
var outDir = Path.Combine(_root, "out");
var temp = Path.Combine(_root, "temp");
var res = conv.Convert(zipPath, "Shapefile", "GeoJson", outDir, temp);
Assert.Equal(ConversionStatus.Failure, res.Status);
Assert.NotEmpty(res.Message);
// Detection/handling of malformed shapefile archives can vary depending on helper behavior; assert non-empty message
// and that it mentions either archive requirements or an unexpected error so the test is resilient to implementation details.
var msg = res.Message ?? string.Empty;
Assert.True(
msg.IndexOf("archive", StringComparison.OrdinalIgnoreCase) >= 0
|| msg.IndexOf("does not contain", StringComparison.OrdinalIgnoreCase) >= 0
|| msg.IndexOf("unexpected", StringComparison.OrdinalIgnoreCase) >= 0
|| msg.IndexOf("missing", StringComparison.OrdinalIgnoreCase) >= 0,
$"Unexpected detect message: {msg}");
}
private static bool IntegrationEnabled() => string.Equals(Environment.GetEnvironmentVariable("GITCONVERTER_ENABLE_INTEGRATION"), "1", StringComparison.Ordinal);
[Fact(DisplayName = "SingleFileConversion_CommonFormatPairs_Integration")]
public void SingleFileConversion_CommonFormatPairs_Integration()
{
if (!IntegrationEnabled()) return;
var conv = new UniversalGisConverter();
// GeoJson -> Shapefile
var geo = Path.Combine(_root, "p.geojson");
File.WriteAllText(geo, "{ \"type\": \"FeatureCollection\", \"features\": [] }");
var outDir = Path.Combine(_root, "out_geo");
var temp = Path.Combine(_root, "temp_geo");
var res = conv.Convert(geo, "GeoJson", "Shapefile", outDir, temp);
Assert.Equal(ConversionStatus.Success, res.Status);
var maybeExt = FileExtensionHelpers.FromOption("Shapefile");
var dot = FileExtensionHelpers.ToDotExtension(maybeExt ?? FileExtension.Shapefile);
Assert.True(Directory.Exists(outDir));
Assert.NotEmpty(Directory.GetFiles(outDir, "*" + dot, SearchOption.AllDirectories));
// CSV -> Kml
var csv = Path.Combine(_root, "c.csv");
File.WriteAllText(csv, "latitude,longitude\n0,0\n");
var outDir2 = Path.Combine(_root, "out_csv");
var temp2 = Path.Combine(_root, "temp_csv");
var res2 = conv.Convert(csv, "Csv", "Kml", outDir2, temp2);
Assert.Equal(ConversionStatus.Success, res2.Status);
var maybeExt2 = FileExtensionHelpers.FromOption("Kml");
var dot2 = FileExtensionHelpers.ToDotExtension(maybeExt2 ?? FileExtension.Kml);
Assert.True(Directory.Exists(outDir2));
Assert.NotEmpty(Directory.GetFiles(outDir2, "*" + dot2, SearchOption.AllDirectories));
}
[Fact(DisplayName = "ArchiveConversion_ShapefileArchive_Integration")]
public void ArchiveConversion_ShapefileArchive_Integration()
{
if (!IntegrationEnabled()) return;
var conv = new UniversalGisConverter();
// Create shapefile components together
var baseDir = Path.Combine(_root, "shp_src");
Directory.CreateDirectory(baseDir);
File.WriteAllText(Path.Combine(baseDir, "a.shp"), "shp");
File.WriteAllText(Path.Combine(baseDir, "a.shx"), "shx");
File.WriteAllText(Path.Combine(baseDir, "a.dbf"), "dbf");
var zipPath = Path.Combine(_root, "shp.zip");
ZipFile.CreateFromDirectory(baseDir, zipPath);
var outDir = Path.Combine(_root, "out_shp");
var temp = Path.Combine(_root, "temp_shp");
var res = conv.Convert(zipPath, "Shapefile", "GeoJson", outDir, temp);
Assert.Equal(ConversionStatus.Success, res.Status);
var maybeExt = FileExtensionHelpers.FromOption("GeoJson");
var dot = FileExtensionHelpers.ToDotExtension(maybeExt ?? FileExtension.GeoJson);
Assert.True(Directory.Exists(outDir));
Assert.NotEmpty(Directory.GetFiles(outDir, "*" + dot, SearchOption.AllDirectories));
}
[Fact(DisplayName = "MissingRequiredDbfInShapefileArchive_ReturnsFailure")]
public void MissingRequiredDbfInShapefileArchive_ReturnsFailure()
{
var conv = new UniversalGisConverter();
var baseDir = Path.Combine(_root, "shp_incomplete");
Directory.CreateDirectory(baseDir);
File.WriteAllText(Path.Combine(baseDir, "a.shp"), "shp");
File.WriteAllText(Path.Combine(baseDir, "a.shx"), "shx");
// missing dbf
var zipPath = Path.Combine(_root, "shp_incomplete.zip");
ZipFile.CreateFromDirectory(baseDir, zipPath);
var outDir = Path.Combine(_root, "out_incomplete");
var temp = Path.Combine(_root, "temp_incomplete");
var res = conv.Convert(zipPath, "Shapefile", "GeoJson", outDir, temp);
Assert.Equal(ConversionStatus.Failure, res.Status);
// Message can vary; accept archive-specific or generic messages
var msg = (res.Message ?? string.Empty).ToLowerInvariant();
Assert.True(
msg.Contains("archive")
|| msg.Contains("does not contain")
|| msg.Contains("missing")
|| msg.Contains("unexpected")
|| msg.Contains("argument")
|| msg.Contains("invalid"),
$"Unexpected message: {res.Message}");
}
[Fact(DisplayName = "InvalidSourceFormat_CorruptedFile_ReturnsFailure")]
public void InvalidSourceFormat_CorruptedFile_ReturnsFailure()
{
var conv = new UniversalGisConverter();
var input = Path.Combine(_root, "bad.geojson");
File.WriteAllText(input, "{ \"type\": \"FeatureCollection\", \"features\": [ { \"type\": \"Feature\" "); // truncated
var outDir = Path.Combine(_root, "out_bad");
var temp = Path.Combine(_root, "temp_bad");
var res = conv.Convert(input, "GeoJson", "GeoJson", outDir, temp);
Assert.Equal(ConversionStatus.Failure, res.Status);
// Converter may report parsing errors or bubble unexpected exceptions; accept either a parsing-specific
// message or a generic unexpected error message to keep the test robust across implementations.
var msg = (res.Message ?? string.Empty).ToLowerInvariant();
Assert.True(
msg.Contains("could not be determined")
|| msg.Contains("could not parse")
|| msg.Contains("corrupt")
|| msg.Contains("truncated")
|| msg.Contains("unexpected")
|| msg.Contains("argument")
|| msg.Contains("invalid"),
$"Unexpected message: {res.Message}");
}
[Fact(DisplayName = "InvalidTargetFormat_Unsupported_ReturnsFailure")]
public void InvalidTargetFormat_Unsupported_ReturnsFailure()
{
var conv = new UniversalGisConverter();
var input = Path.Combine(_root, "a.geojson");
File.WriteAllText(input, "{ \"type\": \"FeatureCollection\", \"features\": [] }");
var outDir = Path.Combine(_root, "out_badtarget");
var temp = Path.Combine(_root, "temp_badtarget");
var res = conv.Convert(input, "GeoJson", "NoSuchTargetFormat", outDir, temp);
Assert.Equal(ConversionStatus.Failure, res.Status);
Assert.Contains("Invalid source or destination format", res.Message, StringComparison.OrdinalIgnoreCase);
}
[Fact(DisplayName = "FormatSpecificOptions_Gml_RestoreSchema")]
public void FormatSpecificOptions_Gml_RestoreSchema()
{
// This test inspects options built by ConverterUtils rather than executing a full conversion.
var options = ConverterUtils.BuildConversionOptions("Gml", "Gml");
Assert.NotNull(options);
// If options expose a key for RestoreSchema check it exists; otherwise the test ensures options are non-null
// (specific option names depend on Aspose driver API; this assertion is conservative).
}
[Fact(DisplayName = "TempFolderCleanup_AfterSuccessfulConversion_Integration")]
public void TempFolderCleanup_AfterSuccessfulConversion_Integration()
{
if (!IntegrationEnabled()) return;
var conv = new UniversalGisConverter();
// Create a simple geojson and perform conversion to a format that will produce output
var geo = Path.Combine(_root, "p2.geojson");
File.WriteAllText(geo, "{ \"type\": \"FeatureCollection\", \"features\": [] }");
var outDir = Path.Combine(_root, "out_temp");
var temp = Path.Combine(_root, "temp_temp");
var res = conv.Convert(geo, "GeoJson", "Shapefile", outDir, temp);
Assert.Equal(ConversionStatus.Success, res.Status);
// When the converter extracted to temp it should attempt cleanup. Verify the temp folder is not left behind.
Assert.False(Directory.Exists(temp), "Expected temporary folder to be removed after conversion.");
}
[Fact(DisplayName = "OutputFile_HasCorrectExtension_Integration")]
public void OutputFile_HasCorrectExtension_Integration()
{
if (!IntegrationEnabled()) return;
var conv = new UniversalGisConverter();
var geo = Path.Combine(_root, "p3.geojson");
File.WriteAllText(geo, "{ \"type\": \"FeatureCollection\", \"features\": [] }");
var outDir = Path.Combine(_root, "out_ext");
var temp = Path.Combine(_root, "temp_ext");
var res = conv.Convert(geo, "GeoJson", "GeoJson", outDir, temp);
Assert.Equal(ConversionStatus.Success, res.Status);
var ext = FileExtensionHelpers.FromOption("GeoJson");
var dot = FileExtensionHelpers.ToDotExtension(ext ?? FileExtension.GeoJson);
var files = Directory.GetFiles(outDir, "*" + dot, SearchOption.AllDirectories);
Assert.NotEmpty(files);
}
}
}