test_geocode_helpers.rb 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. require "minitest/autorun"
  2. require_relative "support/stubs"
  3. require_relative "../lib/geocode"
  4. # Tests for the pure helper methods in Geocode that do not require a DB
  5. # connection or network access. All DB/Http calls are stubbed out via
  6. # test/support/stubs.rb.
  7. class TestGeocodeCleanForGeocode < Minitest::Test
  8. def test_removes_ct_reference_with_colon
  9. input = "123 Main St (CT: 12345/678)"
  10. result = Geocode.clean_for_geocode(input)
  11. assert_equal "123 Main St", result
  12. end
  13. def test_removes_ct_reference_with_space
  14. input = "45 High St (CT 99/100)"
  15. result = Geocode.clean_for_geocode(input)
  16. assert_equal "45 High St", result
  17. end
  18. def test_collapses_whitespace
  19. result = Geocode.clean_for_geocode("12 George St")
  20. assert_equal "12 George St", result
  21. end
  22. def test_strips_leading_and_trailing_whitespace
  23. result = Geocode.clean_for_geocode(" 4 River Rd ")
  24. assert_equal "4 River Rd", result
  25. end
  26. def test_plain_address_unchanged
  27. result = Geocode.clean_for_geocode("7 Beach Rd, Hobart")
  28. assert_equal "7 Beach Rd, Hobart", result
  29. end
  30. def test_empty_string
  31. assert_equal "", Geocode.clean_for_geocode("")
  32. end
  33. end
  34. class TestGeocodeParseResult < Minitest::Test
  35. # Build a minimal Google Maps geocode result hash
  36. def result_fixture(components:, location: { "lat" => -42.8, "lng" => 147.3 }, location_type: "ROOFTOP", types: ["street_address"])
  37. {
  38. "types" => types,
  39. "address_components" => components,
  40. "geometry" => {
  41. "location" => location,
  42. "location_type" => location_type
  43. }
  44. }
  45. end
  46. def test_full_address
  47. components = [
  48. { "types" => ["street_number"], "long_name" => "42", "short_name" => "42" },
  49. { "types" => ["route"], "long_name" => "Main Street", "short_name" => "Main St" },
  50. { "types" => ["locality"], "long_name" => "Hobart", "short_name" => "Hobart" },
  51. { "types" => ["administrative_area_level_1"], "long_name" => "Tasmania", "short_name" => "TAS" },
  52. { "types" => ["postal_code"], "long_name" => "7000", "short_name" => "7000" }
  53. ]
  54. r = Geocode.parse_result(result_fixture(components: components))
  55. assert_equal "42 Main Street, Hobart, TAS, 7000", r[:display]
  56. assert_equal "42 Main Street", r[:street]
  57. assert_equal "Hobart", r[:locality]
  58. assert_equal "TAS", r[:state]
  59. assert_equal "7000", r[:postcode]
  60. assert_in_delta(-42.8, r[:lat], 0.001)
  61. assert_in_delta(147.3, r[:lng], 0.001)
  62. end
  63. def test_missing_street_number
  64. components = [
  65. { "types" => ["route"], "long_name" => "Collins Street", "short_name" => "Collins St" },
  66. { "types" => ["locality"], "long_name" => "Launceston", "short_name" => "Launceston" },
  67. { "types" => ["administrative_area_level_1"], "long_name" => "Tasmania", "short_name" => "TAS" },
  68. { "types" => ["postal_code"], "long_name" => "7250", "short_name" => "7250" }
  69. ]
  70. r = Geocode.parse_result(result_fixture(components: components))
  71. assert_equal "Collins Street", r[:street]
  72. assert_equal "Collins Street, Launceston, TAS, 7250", r[:display]
  73. end
  74. def test_postal_town_fallback_for_locality
  75. # When no "locality" component, fall back to "postal_town"
  76. components = [
  77. { "types" => ["street_number"], "long_name" => "5", "short_name" => "5" },
  78. { "types" => ["route"], "long_name" => "Hill Rd", "short_name" => "Hill Rd" },
  79. { "types" => ["postal_town"], "long_name" => "Burnie", "short_name" => "Burnie" },
  80. { "types" => ["administrative_area_level_1"], "long_name" => "Tasmania", "short_name" => "TAS" },
  81. { "types" => ["postal_code"], "long_name" => "7320", "short_name" => "7320" }
  82. ]
  83. r = Geocode.parse_result(result_fixture(components: components))
  84. assert_equal "Burnie", r[:locality]
  85. end
  86. def test_empty_components_returns_safe_defaults
  87. r = Geocode.parse_result(result_fixture(components: []))
  88. assert_equal "", r[:street]
  89. assert_equal "", r[:locality]
  90. assert_equal "", r[:state]
  91. assert_equal "", r[:postcode]
  92. assert_equal "", r[:display]
  93. end
  94. end
  95. class TestGeocodePickBestResult < Minitest::Test
  96. def make_result(location_type:, types:, state: "TAS")
  97. {
  98. "types" => types,
  99. "address_components" => [
  100. { "types" => ["administrative_area_level_1"], "short_name" => state }
  101. ],
  102. "geometry" => { "location_type" => location_type }
  103. }
  104. end
  105. def test_prefers_rooftop_over_approximate
  106. rooftop = make_result(location_type: "ROOFTOP", types: ["street_address"])
  107. approx = make_result(location_type: "APPROXIMATE", types: ["locality"])
  108. best = Geocode.pick_best_result([approx, rooftop])
  109. assert_equal "ROOFTOP", best.dig("geometry", "location_type")
  110. end
  111. def test_prefers_tas_result_over_non_tas
  112. tas_result = make_result(location_type: "APPROXIMATE", types: ["street_address"], state: "TAS")
  113. vic_result = make_result(location_type: "ROOFTOP", types: ["street_address"], state: "VIC")
  114. best = Geocode.pick_best_result([vic_result, tas_result])
  115. tas_state = best["address_components"].find { |c| c["types"].include?("administrative_area_level_1") }&.fetch("short_name")
  116. assert_equal "TAS", tas_state
  117. end
  118. def test_single_result_returned_as_is
  119. r = make_result(location_type: "ROOFTOP", types: ["street_address"])
  120. assert_equal r, Geocode.pick_best_result([r])
  121. end
  122. end