using GitConverter.Lib.Factories; namespace GitConverter.TestsApp.Factories { /// /// Unit tests for normalization and suggestion utilities. /// /// /// /// Purpose /// - Verify behavior of , , /// and (internal, exposed to tests via InternalsVisibleTo). /// /// /// Test strategy /// - Use small, deterministic inputs that exercise normalization, exact matches, near-miss suggestions, /// tie-breaking, threshold behavior and edge-cases such as null/empty inputs and non-ASCII characters. /// - Keep tests fast and isolated (no file I/O or external dependencies). /// /// /// Remarks on algorithmic behavior /// - Normalization lowers case, trims surrounding quotes/whitespace and removes characters outside [a-z0-9]. /// - Suggestions are based on Levenshtein distance with a conservative acceptance threshold to avoid /// spurious suggestions for distant strings. /// - LevenshteinDistance implementation is space-optimized and expected to be symmetric: distance(a,b) == distance(b,a). /// /// /// Testing guidance /// - When adapting normalization to support Unicode or transliteration update tests accordingly. /// - SuggestClosest deterministic selection on ties depends on iteration order of candidates; tests assume /// the first matching minimal-distance candidate is returned. /// /// public class FactoryHelpersTests { [Theory] [InlineData("Esri-Json", "esrijson")] [InlineData(" ESRI_JSON ", "esrijson")] [InlineData("GeoJsonSeq", "geojsonseq")] [InlineData("Geo Json", "geojson")] [InlineData("\"Kml\"", "kml")] [InlineData(null, "")] [InlineData("", "")] public void NormalizeKey_ReturnsExpectedNormalizedKey(string input, string expected) { var actual = FactoryHelpers.NormalizeKey(input); Assert.Equal(expected, actual); } [Fact] public void NormalizeKey_NonAsciiCharacters_AreStripped() { // Current normalization strips non-ascii characters; ensure it does not throw and returns a reduced key var actual = FactoryHelpers.NormalizeKey("Åland-ÄÖ"); Assert.IsType(actual); // Non-ascii diacritics are stripped leaving the ascii letters 'land' Assert.Equal("land", actual); } [Fact] public void SuggestClosest_NullTarget_ReturnsNull() { var candidates = new[] { "EsriJson", "GeoJson" }; var result = FactoryHelpers.SuggestClosest(null, candidates); Assert.Null(result); } [Fact] public void SuggestClosest_NullCandidates_ReturnsNull_NoException() { var ex = Record.Exception(() => { var r = FactoryHelpers.SuggestClosest("esrijsn", null); Assert.Null(r); }); Assert.Null(ex); } [Fact] public void SuggestClosest_ExactNormalizedMatch_ReturnsCandidate() { var candidates = new[] { "EsriJson", "GeoJson" }; var result = FactoryHelpers.SuggestClosest("EsriJson", candidates); Assert.Equal("EsriJson", result); } [Fact] public void SuggestClosest_Typo_ReturnsSuggestion() { var candidates = new[] { "EsriJson", "GeoJson" }; var result = FactoryHelpers.SuggestClosest("esrijsn", candidates); Assert.Equal("EsriJson", result); } [Fact] public void SuggestClosest_NoCloseMatch_ReturnsNull() { var candidates = new[] { "EsriJson", "GeoJson", "Shapefile" }; var result = FactoryHelpers.SuggestClosest("zzzz", candidates); Assert.Null(result); } [Fact] public void LevenshteinDistance_BasicCases() { Assert.Equal(3, FactoryHelpers.LevenshteinDistance("kitten", "sitting")); Assert.Equal(3, FactoryHelpers.LevenshteinDistance("", "abc")); Assert.Equal(0, FactoryHelpers.LevenshteinDistance("same", "same")); Assert.Equal(1, FactoryHelpers.LevenshteinDistance("abc", "ab")); } [Fact] public void LevenshteinDistance_IsSymmetric() { var a = "distance"; var b = "distancey"; Assert.Equal(FactoryHelpers.LevenshteinDistance(a, b), FactoryHelpers.LevenshteinDistance(b, a)); } [Fact] public void SuggestClosest_SkipsEmptyCandidates() { var candidates = new[] { " ", null, "GeoJson" }; var result = FactoryHelpers.SuggestClosest("geojson", candidates); Assert.Equal("GeoJson", result); } [Fact] public void SuggestClosest_TieBreaks_ReturnsFirstCandidate() { // Construct two candidates equidistant from target; first should be selected var candidates = new[] { "abcd", "abce" }; var result = FactoryHelpers.SuggestClosest("abcf", candidates); // both candidates have distance 2; implementation should pick the first encountered Assert.Equal("abcd", result); } [Fact] public void SuggestClosest_Threshold_PreventsFarMatches() { var candidates = new[] { "EsriJson", "GeoJson" }; // Make a target that is somewhat long so threshold is higher; use a distant string var target = "this-is-very-different"; var res = FactoryHelpers.SuggestClosest(target, candidates); Assert.Null(res); } } }