using static GitConverter.Lib.Converters.JsonFormatDetector; namespace GitConverter.TestsApp.Converters { /// /// Unit tests for . /// /// /// /// Purpose /// - Verify the heuristic JSON GIS format detection implemented by . /// - Ensure the detector correctly classifies common JSON GIS payloads: GeoJSON, EsriJSON, /// TopoJSON and GeoJSON sequences (NDJSON or array-of-objects). /// /// /// Test strategy /// - For each supported format we create minimal on-disk examples and assert that /// returns the expected value. /// - We validate order-independence (properties appearing in different orders) and /// edge cases such as empty files and missing files. /// - NDJSON detection is exercised with and without a BOM and with leading whitespace. /// /// /// Implementation notes /// - Tests write ephemeral files to a per-test temporary folder under the system temp path /// and remove them in to avoid leaving artifacts on the developer /// machine or CI agent. /// - These are pure unit tests that exercise only the detection heuristics; they do not /// require Aspose or other heavy dependencies. /// /// public class JsonFormatDetectorTests : IDisposable { private readonly string _root; /// /// Create an isolated temp folder for this test instance. /// public JsonFormatDetectorTests() { _root = Path.Combine(Path.GetTempPath(), "GitConverter.JsonFormatTests", Guid.NewGuid().ToString("N")); Directory.CreateDirectory(_root); } /// /// Clean up ephemeral test files and folders created during the tests. /// public void Dispose() { try { if (Directory.Exists(_root)) Directory.Delete(_root, true); } catch { } } /// /// Helper to write a temporary file inside the test folder and return its path. /// /// Relative file name under the test folder. /// File content to write. /// Full path to the written file. private string Write(string name, string content) { var path = Path.Combine(_root, name); File.WriteAllText(path, content); return path; } // ---------- GeoJSON tests ---------- /// /// Minimal GeoJSON FeatureCollection should be detected as GeoJson. /// [Fact(DisplayName = "Detect_GeoJson_Simple")] public void Detect_GeoJson_Simple() { var json = @"{ ""type"": ""FeatureCollection"", ""features"": [] }"; var path = Write("geo_simple.json", json); var result = DetectFromFile(path); Assert.Equal(Format.GeoJson, result); } /// /// A slightly more complex GeoJSON payload with a feature and geometry should be detected as GeoJson. /// [Fact(DisplayName = "Detect_GeoJson_Variant")] public void Detect_GeoJson_Variant() { var json = @"{ ""type"": ""FeatureCollection"", ""features"": [ { ""type"": ""Feature"", ""geometry"": { ""type"": ""Point"", ""coordinates"": [0,0] }, ""properties"": { ""id"": 1 } } ] }"; var path = Write("geo_variant.json", json); var result = DetectFromFile(path); Assert.Equal(Format.GeoJson, result); } /// /// The detector should recognize GeoJSON even if the 'type' property is not the first property. /// [Fact(DisplayName = "Detect_GeoJson_TypeNotFirst")] public void Detect_GeoJson_TypeNotFirst() { // 'features' property before 'type' — detector should find FeatureCollection regardless of order var json = @"{ ""features"": [], ""type"": ""FeatureCollection"" }"; var path = Write("geo_type_not_first.json", json); var result = DetectFromFile(path); Assert.Equal(Format.GeoJson, result); } // ---------- EsriJSON tests ---------- /// /// Esri JSON containing spatialReference and features should be detected as EsriJson. /// [Fact(DisplayName = "Detect_EsriJson_Simple")] public void Detect_EsriJson_Simple() { var json = @"{ ""spatialReference"": { ""wkid"": 4326 }, ""features"": [] }"; var path = Write("esri_simple.json", json); var result = DetectFromFile(path); Assert.Equal(Format.EsriJson, result); } /// /// Variant Esri JSON exercise geometryType and spatialReference detection. /// [Fact(DisplayName = "Detect_EsriJson_Variant")] public void Detect_EsriJson_Variant() { var json = @"{ ""features"": [], ""geometryType"": ""esriGeometryPoint"", ""spatialReference"": { ""wkt"": ""GEOGCS[...]"" } }"; var path = Write("esri_variant.json", json); var result = DetectFromFile(path); Assert.Equal(Format.EsriJson, result); } /// /// Detector handles order-independence between 'features' and 'spatialReference' properties. /// [Fact(DisplayName = "Detect_EsriJson_OrderIndependence")] public void Detect_EsriJson_OrderIndependence() { // 'features' appears before 'spatialReference' or vice versa - detector should handle either order var json = @"{ ""features"": [], ""otherProp"": 1, ""spatialReference"": { ""wkid"": 3857 } }"; var path = Write("esri_order.json", json); var result = DetectFromFile(path); Assert.Equal(Format.EsriJson, result); } // ---------- GeoJSON Sequence / NDJSON tests ---------- /// /// A top-level array of objects should be classified as GeoJsonSeq. /// [Fact(DisplayName = "Detect_GeoJsonSeq_Array")] public void Detect_GeoJsonSeq_Array() { var json = @"[ { ""type"": ""Feature"" }, { ""type"": ""Feature"" } ]"; var path = Write("seq_array.json", json); var result = DetectFromFile(path); Assert.Equal(Format.GeoJsonSeq, result); } /// /// Newline-delimited JSON (two lines) should be detected as GeoJsonSeq by probing first line when full-parse fails. /// [Fact(DisplayName = "Detect_GeoJsonSeq_NDJSON")] public void Detect_GeoJsonSeq_NDJSON() { // NDJSON: newline-delimited JSON objects (we put two lines) var json = "{ \"type\": \"Feature\" }\n{ \"type\": \"Feature\" }\n"; var path = Write("seq_ndjson.json", json); // DetectFromFile attempts to parse and falls back to first-line parsing for NDJSON var result = DetectFromFile(path); Assert.Equal(Format.GeoJsonSeq, result); } /// /// NDJSON with a BOM and leading blank lines should still be detected as GeoJsonSeq. /// [Fact(DisplayName = "Detect_GeoJsonSeq_NDJSON_WithBOM_AndWhitespace")] public void Detect_GeoJsonSeq_NDJSON_WithBOM_AndWhitespace() { // Leading BOM and blank lines before NDJSON content should still be detected as GeoJsonSeq var json = "\uFEFF\n\n{ \"type\": \"Feature\" }\n{ \"type\": \"Feature\" }\n"; var path = Write("seq_ndjson_bom.json", json); var result = DetectFromFile(path); Assert.Equal(Format.GeoJsonSeq, result); } // ---------- TopoJSON tests ---------- /// /// TopoJSON documents with "type":"Topology" should be identified as TopoJson. /// [Fact(DisplayName = "Detect_TopoJson_Simple")] public void Detect_TopoJson_Simple() { var json = @"{ ""type"": ""Topology"", ""objects"": {} }"; var path = Write("topo_simple.json", json); var result = DetectFromFile(path); Assert.Equal(Format.TopoJson, result); } /// /// Variant TopoJSON with nested objects should also be recognized. /// [Fact(DisplayName = "Detect_TopoJson_Variant")] public void Detect_TopoJson_Variant() { var json = @"{ ""type"": ""Topology"", ""objects"": { ""example"": { ""type"": ""GeometryCollection"", ""geometries"": [] } } }"; var path = Write("topo_variant.json", json); var result = DetectFromFile(path); Assert.Equal(Format.TopoJson, result); } // ---------- Edge cases ---------- /// /// Empty files should be classified as Unknown. /// [Fact(DisplayName = "Detect_Unknown_Empty")] public void Detect_Unknown_Empty() { var path = Write("empty.json", string.Empty); var result = DetectFromFile(path); Assert.Equal(Format.Unknown, result); } /// /// TryDetectFromFile should return false for missing files and set the out format to Unknown. /// [Fact(DisplayName = "TryDetectFromFile_ReturnsFalse_OnMissingFile")] public void TryDetectFromFile_ReturnsFalse_OnMissingFile() { var missing = Path.Combine(_root, "does-not-exist.json"); Assert.False(File.Exists(missing)); var ok = TryDetectFromFile(missing, out var fmt); Assert.False(ok); Assert.Equal(Format.Unknown, fmt); } } }