diff --git a/src/Oms.Tests/GeoPartitionMatcherTests.cs b/src/Oms.Tests/GeoPartitionMatcherTests.cs index 567998a..98d8d3a 100644 --- a/src/Oms.Tests/GeoPartitionMatcherTests.cs +++ b/src/Oms.Tests/GeoPartitionMatcherTests.cs @@ -118,6 +118,110 @@ public class GeoPartitionMatcherTests act.Should().Throw().WithMessage("*州*"); } + [Theory] + [InlineData("BT9 7AA", "BT97AA")] + [InlineData("bt97aa", "BT97AA")] + [InlineData("BT9-7AA", "BT97AA")] + [InlineData("B46 3AB", "B463AB")] + [InlineData(" b46 3ab ", "B463AB")] + public void NormalizeZip_AlphanumericWithSpaces_ShouldStripAndUpper(string raw, string expected) + { + GeoPartitionMatcher.NormalizeZip(raw).Should().Be(expected); + } + + [Fact] + public void Gb_Bt97aa_LongerOutwardPrefixWins() + { + var rules = CompileEntities( + Zip("GB", "BT"), + Zip("GB", "BT9"), + Zip("GB", "BT97"), + Zip("GB", "B")); + + var hit = GeoPartitionMatcher.MatchEntity(rules, "GB", "NIR", "Belfast", "BT9 7AA"); + hit!.LeftPostcode.Should().Be("BT97"); + GeoPartitionMatcher.MatchEntity(rules, "gb", null, null, "BT97AA")!.LeftPostcode.Should().Be("BT97"); + } + + [Fact] + public void Gb_Bt97aa_ExactIgnoresSpaces() + { + var rules = CompileEntities(Exact("GB", "BT9 7AA")); + + GeoPartitionMatcher.IsHit(rules, "GB", null, null, "BT97AA").Should().BeTrue(); + GeoPartitionMatcher.IsHit(rules, "GB", null, null, "BT9 7AA").Should().BeTrue(); + GeoPartitionMatcher.IsHit(rules, "GB", null, null, "BT9 7AB").Should().BeFalse(); + GeoPartitionMatcher.IsHit(rules, "GB", null, null, "BT98AA").Should().BeFalse(); + } + + [Fact] + public void Gb_Bt97aa_ClosedAlphanumericRange() + { + var rules = CompileEntities(Range("GB", "BT90AA", "BT99ZZ")); + + GeoPartitionMatcher.IsHit(rules, "GB", null, null, "BT97AA").Should().BeTrue(); + GeoPartitionMatcher.IsHit(rules, "GB", null, null, "BT9 7AA").Should().BeTrue(); + GeoPartitionMatcher.IsHit(rules, "GB", null, null, "BT90AA").Should().BeTrue(); + GeoPartitionMatcher.IsHit(rules, "GB", null, null, "BT99ZZ").Should().BeTrue(); + GeoPartitionMatcher.IsHit(rules, "GB", null, null, "BT8 1AA").Should().BeFalse(); + GeoPartitionMatcher.IsHit(rules, "GB", null, null, "E1 6AN").Should().BeFalse(); + } + + [Fact] + public void Ca_B463ab_PrefixAndSpaceVariants() + { + var rules = CompileEntities( + Zip("CA", "B"), + Zip("CA", "B4"), + Zip("CA", "B46"), + Zip("CA", "B463")); + + var hit = GeoPartitionMatcher.MatchEntity(rules, "CA", "ON", "Toronto", "B46 3AB"); + hit!.LeftPostcode.Should().Be("B463"); + GeoPartitionMatcher.IsHit(rules, "CA", null, null, "B463AB").Should().BeTrue(); + GeoPartitionMatcher.MatchEntity(rules, "CA", null, null, "B47 3AB")!.LeftPostcode.Should().Be("B4"); + GeoPartitionMatcher.IsHit(rules, "CA", null, null, "M5V 2T6").Should().BeFalse(); + } + + [Fact] + public void Ca_B463ab_ExactAndRange() + { + var exact = CompileEntities(Exact("CA", "B46 3AB")); + var range = CompileEntities(Range("CA", "B460AA", "B469ZZ")); + + GeoPartitionMatcher.IsHit(exact, "CA", null, null, "B46 3AB").Should().BeTrue(); + GeoPartitionMatcher.IsHit(exact, "CA", null, null, "B463AB").Should().BeTrue(); + GeoPartitionMatcher.IsHit(exact, "CA", null, null, "B46 3AC").Should().BeFalse(); + + GeoPartitionMatcher.IsHit(range, "CA", null, null, "B46 3AB").Should().BeTrue(); + GeoPartitionMatcher.IsHit(range, "CA", null, null, "B46 0AA").Should().BeTrue(); + GeoPartitionMatcher.IsHit(range, "CA", null, null, "B47 3AB").Should().BeFalse(); + } + + [Fact] + public void GbAndCa_SameLookingPostcode_CountryMustMatch() + { + var gb = CompileEntities(Zip("GB", "B46"), Exact("GB", "BT97AA")); + var ca = CompileEntities(Zip("CA", "B46")); + + GeoPartitionMatcher.IsHit(gb, "GB", null, null, "B46 3AB").Should().BeTrue(); + GeoPartitionMatcher.IsHit(gb, "CA", null, null, "B46 3AB").Should().BeFalse(); + GeoPartitionMatcher.IsHit(ca, "CA", null, null, "B46 3AB").Should().BeTrue(); + GeoPartitionMatcher.IsHit(ca, "GB", null, null, "BT97AA").Should().BeFalse(); + GeoPartitionMatcher.IsHit(gb, "GB", null, null, "BT97AA").Should().BeTrue(); + } + + [Fact] + public void Gb_Bt97aa_ZipPresent_ShouldSkipState() + { + var rules = CompileEntities( + State("GB", "NIR"), + Zip("GB", "BT1")); + + GeoPartitionMatcher.MatchEntity(rules, "GB", "NIR", "Belfast", "BT97AA").Should().BeNull(); + GeoPartitionMatcher.MatchEntity(rules, "GB", "NIR", "Belfast", null)!.Province.Should().Be("NIR"); + } + private static List CompileEntities(params KpiAreaInfo[] items) { foreach (var item in items) @@ -131,6 +235,9 @@ public class GeoPartitionMatcherTests private static KpiAreaInfo Zip(string country, string prefix) => new() { CountryCode = country, LeftPostcode = prefix }; + private static KpiAreaInfo Exact(string country, string postcode) => + new() { CountryCode = country, LeftPostcode = postcode, RightPostcode = postcode }; + private static KpiAreaInfo Range(string country, string left, string right) => new() { CountryCode = country, LeftPostcode = left, RightPostcode = right };