|
@@ -0,0 +1,140 @@
|
|
|
|
|
+require "minitest/autorun"
|
|
|
|
|
+require_relative "support/stubs"
|
|
|
|
|
+require_relative "../lib/geocode"
|
|
|
|
|
+
|
|
|
|
|
+# Tests for the pure helper methods in Geocode that do not require a DB
|
|
|
|
|
+# connection or network access. All DB/Http calls are stubbed out via
|
|
|
|
|
+# test/support/stubs.rb.
|
|
|
|
|
+
|
|
|
|
|
+class TestGeocodeCleanForGeocode < Minitest::Test
|
|
|
|
|
+ def test_removes_ct_reference_with_colon
|
|
|
|
|
+ input = "123 Main St (CT: 12345/678)"
|
|
|
|
|
+ result = Geocode.clean_for_geocode(input)
|
|
|
|
|
+ assert_equal "123 Main St", result
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ def test_removes_ct_reference_with_space
|
|
|
|
|
+ input = "45 High St (CT 99/100)"
|
|
|
|
|
+ result = Geocode.clean_for_geocode(input)
|
|
|
|
|
+ assert_equal "45 High St", result
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ def test_collapses_whitespace
|
|
|
|
|
+ result = Geocode.clean_for_geocode("12 George St")
|
|
|
|
|
+ assert_equal "12 George St", result
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ def test_strips_leading_and_trailing_whitespace
|
|
|
|
|
+ result = Geocode.clean_for_geocode(" 4 River Rd ")
|
|
|
|
|
+ assert_equal "4 River Rd", result
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ def test_plain_address_unchanged
|
|
|
|
|
+ result = Geocode.clean_for_geocode("7 Beach Rd, Hobart")
|
|
|
|
|
+ assert_equal "7 Beach Rd, Hobart", result
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ def test_empty_string
|
|
|
|
|
+ assert_equal "", Geocode.clean_for_geocode("")
|
|
|
|
|
+ end
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+class TestGeocodeParseResult < Minitest::Test
|
|
|
|
|
+ # Build a minimal Google Maps geocode result hash
|
|
|
|
|
+ def result_fixture(components:, location: { "lat" => -42.8, "lng" => 147.3 }, location_type: "ROOFTOP", types: ["street_address"])
|
|
|
|
|
+ {
|
|
|
|
|
+ "types" => types,
|
|
|
|
|
+ "address_components" => components,
|
|
|
|
|
+ "geometry" => {
|
|
|
|
|
+ "location" => location,
|
|
|
|
|
+ "location_type" => location_type
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ def test_full_address
|
|
|
|
|
+ components = [
|
|
|
|
|
+ { "types" => ["street_number"], "long_name" => "42", "short_name" => "42" },
|
|
|
|
|
+ { "types" => ["route"], "long_name" => "Main Street", "short_name" => "Main St" },
|
|
|
|
|
+ { "types" => ["locality"], "long_name" => "Hobart", "short_name" => "Hobart" },
|
|
|
|
|
+ { "types" => ["administrative_area_level_1"], "long_name" => "Tasmania", "short_name" => "TAS" },
|
|
|
|
|
+ { "types" => ["postal_code"], "long_name" => "7000", "short_name" => "7000" }
|
|
|
|
|
+ ]
|
|
|
|
|
+ r = Geocode.parse_result(result_fixture(components: components))
|
|
|
|
|
+
|
|
|
|
|
+ assert_equal "42 Main Street, Hobart, TAS, 7000", r[:display]
|
|
|
|
|
+ assert_equal "42 Main Street", r[:street]
|
|
|
|
|
+ assert_equal "Hobart", r[:locality]
|
|
|
|
|
+ assert_equal "TAS", r[:state]
|
|
|
|
|
+ assert_equal "7000", r[:postcode]
|
|
|
|
|
+ assert_in_delta(-42.8, r[:lat], 0.001)
|
|
|
|
|
+ assert_in_delta(147.3, r[:lng], 0.001)
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ def test_missing_street_number
|
|
|
|
|
+ components = [
|
|
|
|
|
+ { "types" => ["route"], "long_name" => "Collins Street", "short_name" => "Collins St" },
|
|
|
|
|
+ { "types" => ["locality"], "long_name" => "Launceston", "short_name" => "Launceston" },
|
|
|
|
|
+ { "types" => ["administrative_area_level_1"], "long_name" => "Tasmania", "short_name" => "TAS" },
|
|
|
|
|
+ { "types" => ["postal_code"], "long_name" => "7250", "short_name" => "7250" }
|
|
|
|
|
+ ]
|
|
|
|
|
+ r = Geocode.parse_result(result_fixture(components: components))
|
|
|
|
|
+
|
|
|
|
|
+ assert_equal "Collins Street", r[:street]
|
|
|
|
|
+ assert_equal "Collins Street, Launceston, TAS, 7250", r[:display]
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ def test_postal_town_fallback_for_locality
|
|
|
|
|
+ # When no "locality" component, fall back to "postal_town"
|
|
|
|
|
+ components = [
|
|
|
|
|
+ { "types" => ["street_number"], "long_name" => "5", "short_name" => "5" },
|
|
|
|
|
+ { "types" => ["route"], "long_name" => "Hill Rd", "short_name" => "Hill Rd" },
|
|
|
|
|
+ { "types" => ["postal_town"], "long_name" => "Burnie", "short_name" => "Burnie" },
|
|
|
|
|
+ { "types" => ["administrative_area_level_1"], "long_name" => "Tasmania", "short_name" => "TAS" },
|
|
|
|
|
+ { "types" => ["postal_code"], "long_name" => "7320", "short_name" => "7320" }
|
|
|
|
|
+ ]
|
|
|
|
|
+ r = Geocode.parse_result(result_fixture(components: components))
|
|
|
|
|
+ assert_equal "Burnie", r[:locality]
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ def test_empty_components_returns_safe_defaults
|
|
|
|
|
+ r = Geocode.parse_result(result_fixture(components: []))
|
|
|
|
|
+ assert_equal "", r[:street]
|
|
|
|
|
+ assert_equal "", r[:locality]
|
|
|
|
|
+ assert_equal "", r[:state]
|
|
|
|
|
+ assert_equal "", r[:postcode]
|
|
|
|
|
+ assert_equal "", r[:display]
|
|
|
|
|
+ end
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+class TestGeocodePickBestResult < Minitest::Test
|
|
|
|
|
+ def make_result(location_type:, types:, state: "TAS")
|
|
|
|
|
+ {
|
|
|
|
|
+ "types" => types,
|
|
|
|
|
+ "address_components" => [
|
|
|
|
|
+ { "types" => ["administrative_area_level_1"], "short_name" => state }
|
|
|
|
|
+ ],
|
|
|
|
|
+ "geometry" => { "location_type" => location_type }
|
|
|
|
|
+ }
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ def test_prefers_rooftop_over_approximate
|
|
|
|
|
+ rooftop = make_result(location_type: "ROOFTOP", types: ["street_address"])
|
|
|
|
|
+ approx = make_result(location_type: "APPROXIMATE", types: ["locality"])
|
|
|
|
|
+ best = Geocode.pick_best_result([approx, rooftop])
|
|
|
|
|
+ assert_equal "ROOFTOP", best.dig("geometry", "location_type")
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ def test_prefers_tas_result_over_non_tas
|
|
|
|
|
+ tas_result = make_result(location_type: "APPROXIMATE", types: ["street_address"], state: "TAS")
|
|
|
|
|
+ vic_result = make_result(location_type: "ROOFTOP", types: ["street_address"], state: "VIC")
|
|
|
|
|
+ best = Geocode.pick_best_result([vic_result, tas_result])
|
|
|
|
|
+ tas_state = best["address_components"].find { |c| c["types"].include?("administrative_area_level_1") }&.fetch("short_name")
|
|
|
|
|
+ assert_equal "TAS", tas_state
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ def test_single_result_returned_as_is
|
|
|
|
|
+ r = make_result(location_type: "ROOFTOP", types: ["street_address"])
|
|
|
|
|
+ assert_equal r, Geocode.pick_best_result([r])
|
|
|
|
|
+ end
|
|
|
|
|
+end
|