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); } } }