diff --git a/README.md b/README.md index 5deea9a8e817eeef122bad0ca8816e5bfc82b0ad..06dde6029d0e0c8250c6389628ab448921594483 100644 --- a/README.md +++ b/README.md @@ -696,6 +696,16 @@ This uses the PostcodeAnywhere UK Geocode service, this will geocode any string * **Terms of Service**: ? * **Limitations**: No restrictions on use +#### Base Adresse Nationale FR (`:ban_data_gouv_fr`) + +* **API key**: none +* **Quota**: none +* **Region**: FR +* **SSL support**: yes +* **Languages**: en / fr +* **Documentation**: https://adresse.data.gouv.fr/api/ (in french) +* **Terms of Service**: https://adresse.data.gouv.fr/faq/ (in french) +* **Limitations**: [Data licensed under Open Database License (ODbL) (you must provide attribution).](http://openstreetmap.fr/ban) ### IP Address Services diff --git a/lib/geocoder/lookup.rb b/lib/geocoder/lookup.rb index b704d7cd1d50540a3525d7629a8294e96590af6a..b5e9275a51b16dc6c6be3239c761d46a7aa30443 100644 --- a/lib/geocoder/lookup.rb +++ b/lib/geocoder/lookup.rb @@ -47,6 +47,7 @@ module Geocoder :okf, :postcode_anywhere_uk, :geoportail_lu, + :ban_data_gouv_fr, :test, :latlon ] diff --git a/lib/geocoder/lookups/ban_data_gouv_fr.rb b/lib/geocoder/lookups/ban_data_gouv_fr.rb new file mode 100644 index 0000000000000000000000000000000000000000..1aad701fac3e751f5b5c1c150cf8d602ed27c460 --- /dev/null +++ b/lib/geocoder/lookups/ban_data_gouv_fr.rb @@ -0,0 +1,130 @@ +# encoding: utf-8 + +require 'geocoder/lookups/base' +require 'geocoder/results/ban_data_gouv_fr' + +module Geocoder::Lookup + class BanDataGouvFr < Base + + def name + "Base Adresse Nationale Française" + end + + def map_link_url(coordinates) + "https://www.openstreetmap.org/#map=19/#{coordinates.join('/')}" + end + + def query_url(query) + method = query.reverse_geocode? ? "reverse" : "search" + "#{protocol}://api-adresse.data.gouv.fr/#{method}/?" + url_query_string(query) + end + + private # --------------------------------------------------------------- + + def any_result?(query) + fetch_data(query)['features'].any? + end + + def results(query) + if doc = fetch_data(query) and any_result?(doc) + [doc] + else + [] + end + end + + #### PARAMS #### + + def query_url_params(query) + query_ban_datagouv_fr_params(query).merge(super) + end + + def query_ban_datagouv_fr_params(query) + query.reverse_geocode? ? reverse_geocode_ban_fr_params(query) : search_geocode_ban_fr_params(query) + end + + #### SEARCH GEOCODING PARAMS #### + # + # :q => required, full text search param) + + # :limit => force limit number of results returned by raw API + # (default = 5) note : only first result is taken + # in account in geocoder + # + # :autocomplete => pass 0 to disable autocomplete treatment of :q + # (default = 1) + # + # :lat => force filter results around specific lat/lon + # + # :lon => force filter results around specific lat/lon + # + # :type => force filter the returned result type + # (check results for a list of accepted types) + # + # :postcode => force filter results on a specific city post code + # + # :citycode => force filter results on a specific city UUID INSEE code + # + # For up to date doc (in french only) : https://adresse.data.gouv.fr/api/ + # + def search_geocode_ban_fr_params(query) + params = { + q: query.sanitized_text + } + unless (limit = query.options[:limit]).nil? || !limit_param_is_valid?(limit) + params[:limit] = limit.to_i + end + unless (autocomplete = query.options[:autocomplete]).nil? || !autocomplete_param_is_valid?(autocomplete) + params[:autocomplete] = autocomplete.to_s + end + unless (type = query.options[:type]).nil? || !type_param_is_valid?(type) + params[:type] = type.downcase + end + unless (postcode = query.options[:postcode]).nil? || !code_param_is_valid?(postcode) + params[:postcode] = postcode.to_s + end + unless (citycode = query.options[:citycode]).nil? || !code_param_is_valid?(citycode) + params[:citycode] = citycode.to_s + end + params + end + + #### REVERSE GEOCODING PARAMS #### + # + # :lat => required + # + # :lon => required + # + # :type => force returned results type + # (check results for a list of accepted types) + # + def reverse_geocode_ban_fr_params(query) + lat_lon = query.coordinates + params = { + lat: lat_lon.first, + lon: lat_lon.last + } + unless (type = query.options[:type]).nil? || !type_param_is_valid?(type) + params[:type] = type.downcase + end + params + end + + def limit_param_is_valid?(param) + param.to_i.positive? + end + + def autocomplete_param_is_valid?(param) + [0,1].include?(param.to_i) + end + + def type_param_is_valid?(param) + %w(housenumber street locality village town city).include?(param.downcase) + end + + def code_param_is_valid?(param) + (1..99999).include?(param.to_i) + end + + end +end diff --git a/lib/geocoder/results/ban_data_gouv_fr.rb b/lib/geocoder/results/ban_data_gouv_fr.rb new file mode 100644 index 0000000000000000000000000000000000000000..0b936b0431237aeaf2e25ecbedd63b8b4e233f4c --- /dev/null +++ b/lib/geocoder/results/ban_data_gouv_fr.rb @@ -0,0 +1,257 @@ +# encoding: utf-8 +require 'geocoder/results/base' + +module Geocoder::Result + class BanDataGouvFr < Base + + #### BASE METHODS #### + + def self.response_attributes + %w[limit attribution version licence type features] + end + + response_attributes.each do |a| + unless method_defined?(a) + define_method a do + @data[a] + end + end + end + + #### BEST RESULT #### + + def result + features[0] if features.any? + end + + #### GEOMETRY #### + + def geometry + result['geometry'] if result + end + + def precision + geometry['type'] if geometry + end + + def coordinates + coords = geometry["coordinates"] + return [coords[1].to_f, coords[0].to_f] + end + + #### PROPERTIES #### + + # List of raw attrbutes returned by BAN data gouv fr API: + # + # :id => [string] UUID of the result, said to be not stable + # atm, based on IGN reference (Institut national de + # l'information géographique et forestière) + # + # :type => [string] result type (housenumber, street, city, + # town, village, locality) + # + # :score => [float] value between 0 and 1 giving result's + # relevancy + # + # :housenumber => [string] street number and extra information + # (bis, ter, A, B) + # + # :street => [string] street name + # + # :name => [string] housenumber and street name + # + # :postcode => [string] city post code (used for mails by La Poste, + # beware many cities got severeal postcodes) + # + # :citycode => [string] city code (INSEE reference, + # consider it as a french institutional UUID) + # + # :city => [string] city name + # + # :context => [string] department code, department name and + # region code + # + # :label => [string] full address without state, country name + # and country code + # + # CITIES ONLY PROPERTIES + # + # :adm_weight => [string] administrative weight (importance) of + # the city + # + # :population => [float] number of inhabitants with a 1000 factor + # + # For up to date doc (in french only) : https://adresse.data.gouv.fr/api/ + # + def properties + result['properties'] if result + end + + # List of usable Geocoder results' methods + # + # score => [float] result relevance 0 to 1 + # + # location_id => [string] location's IGN UUID + # + # result_type => [string] housenumber / street / city + # / town / village / locality + # + # international_address => [string] full address with country code + # + # national_address => [string] full address with country code + # + # street_address => [string] housenumber + extra inf + # + street name + # + # street_number => [string] housenumber + extra inf + # (bis, ter, etc) + # + # street_name => [string] street's name + # + # city_name => [string] city's name + # + # city_code => [string] city's INSEE UUID + # + # postal_code => [string] city's postal code (used for mails) + # + # context => [string] city's department code, department + # name and region name + # + # demartment_name => [string] city's department name + # + # department_code => [string] city's department INSEE UUID + # + # region_name => [string] city's region name + # + # population => [string] city's inhabitants count + # + # administrative_weight => [integer] city's importance on a scale + # from 6 (capital city) to 1 (regular village) + # + def score + properties['score'] + end + + def location_id + properties['id'] + end + + # Types + # + # housenumber + # street + # city + # town + # village + # locality + # + def result_type + properties['type'] + end + + def international_address + "#{national_address}, #{country}" + end + + def national_address + properties['label'] + end + + def street_address + properties['name'] + end + + def street_number + properties['housenumber'] + end + + def street_name + properties['street'] + end + + def city_name + properties['city'] + end + + def city_code + properties['citycode'] + end + + def postal_code + properties['postcode'] + end + + def context + properties['context'].split(/,/).map(&:strip) + end + + def department_code + context[0] if context.length > 0 + end + + # Monkey logic to handle fact Paris is both a city and a department + # in Île-de-France region + def department_name + if context.length > 1 + if context[1] == "Île-de-France" + "Paris" + else + context[1] + end + end + end + + def region_name + if context.length == 2 && context[1] == "Île-de-France" + context[1] + elsif context.length > 2 + context[2] + end + end + + def country + "France" + end + + # Country code types + # FR : France + # GF : Guyane Française + # RE : Réunion + # NC : Nouvelle-Calédonie + # GP : Guadeloupe + # MQ : Martinique + # MU : Maurice + # PF : Polynésie française + # + # Will need refacto to handle different country codes, but BAN API + # is currently mainly designed for geocode FR country code addresses + def country_code + "FR" + end + + #### ALIAS METHODS #### + + alias_method :address, :international_address + alias_method :street, :street_name + alias_method :city, :city_name + alias_method :state, :region_name + alias_method :state_code, :state + + #### CITIES' METHODS #### + + def population + (properties['population'].to_f * 1000).to_i if city?(result_type) + end + + def administrative_weight + properties['adm_weight'].to_i if city?(result_type) + end + + private + + def city?(result_type) + %w(village town city).include?(result_type) + end + + end +end diff --git a/test/fixtures/ban_data_gouv_fr_montpellier b/test/fixtures/ban_data_gouv_fr_montpellier new file mode 100644 index 0000000000000000000000000000000000000000..5000353a707a45f0fce44ecd79842ff448369a75 --- /dev/null +++ b/test/fixtures/ban_data_gouv_fr_montpellier @@ -0,0 +1,125 @@ +{ + "limit": 5, + "attribution": "BAN", + "version": "draft", + "licence": "ODbL 1.0", + "query": "Montpellier", + "type": "FeatureCollection", + "features": [ + { + "geometry": { + "type": "Point", + "coordinates": [ + 3.875521, + 43.611024 + ] + }, + "properties": { + "citycode": "34172", + "adm_weight": "5", + "name": "Montpellier", + "city": "Montpellier", + "postcode": "34080", + "context": "34, Hérault, Languedoc-Roussillon", + "score": 0.9785090909090908, + "label": "Montpellier", + "id": "34172_34080", + "type": "city", + "population": "255.1" + }, + "type": "Feature" + }, + { + "geometry": { + "type": "Point", + "coordinates": [ + 3.875521, + 43.611024 + ] + }, + "properties": { + "citycode": "34172", + "adm_weight": "5", + "name": "Montpellier", + "city": "Montpellier", + "postcode": "34000", + "context": "34, Hérault, Languedoc-Roussillon", + "score": 0.9785090909090908, + "label": "Montpellier", + "id": "34172", + "type": "city", + "population": "255.1" + }, + "type": "Feature" + }, + { + "geometry": { + "type": "Point", + "coordinates": [ + 3.875521, + 43.611024 + ] + }, + "properties": { + "citycode": "34172", + "adm_weight": "5", + "name": "Montpellier", + "city": "Montpellier", + "postcode": "34090", + "context": "34, Hérault, Languedoc-Roussillon", + "score": 0.9785090909090908, + "label": "Montpellier", + "id": "34172_34090", + "type": "city", + "population": "255.1" + }, + "type": "Feature" + }, + { + "geometry": { + "type": "Point", + "coordinates": [ + 3.875521, + 43.611024 + ] + }, + "properties": { + "citycode": "34172", + "adm_weight": "5", + "name": "Montpellier", + "city": "Montpellier", + "postcode": "34070", + "context": "34, Hérault, Languedoc-Roussillon", + "score": 0.9785090909090908, + "label": "Montpellier", + "id": "34172_34070", + "type": "city", + "population": "255.1" + }, + "type": "Feature" + }, + { + "geometry": { + "type": "Point", + "coordinates": [ + -0.744328, + 45.634502 + ] + }, + "properties": { + "citycode": "17244", + "adm_weight": "1", + "name": "Montpellier-de-Médillan", + "city": "Montpellier-de-Médillan", + "postcode": "17260", + "context": "17, Charente-Maritime, Poitou-Charentes", + "score": 0.825, + "label": "Montpellier-de-Médillan", + "id": "17244", + "type": "village", + "population": "0.6" + }, + "type": "Feature" + } + ] +} diff --git a/test/fixtures/ban_data_gouv_fr_no_results b/test/fixtures/ban_data_gouv_fr_no_results new file mode 100644 index 0000000000000000000000000000000000000000..3c2e78333984f1e07799b09f26ae8747ed4a148c --- /dev/null +++ b/test/fixtures/ban_data_gouv_fr_no_results @@ -0,0 +1,9 @@ +{ + "limit": 5, + "attribution": "BAN", + "version": "draft", + "licence": "ODbL 1.0", + "query": "oozpuip", + "type": "FeatureCollection", + "features": [] +} diff --git a/test/fixtures/ban_data_gouv_fr_no_reverse_results b/test/fixtures/ban_data_gouv_fr_no_reverse_results new file mode 100644 index 0000000000000000000000000000000000000000..657d238f46ad1dde95e4e9a06a97b931b95161cb --- /dev/null +++ b/test/fixtures/ban_data_gouv_fr_no_reverse_results @@ -0,0 +1,8 @@ +{ + "limit": 1, + "attribution": "BAN", + "version": "draft", + "licence": "ODbL 1.0", + "type": "FeatureCollection", + "features": [] +} diff --git a/test/fixtures/ban_data_gouv_fr_paris b/test/fixtures/ban_data_gouv_fr_paris new file mode 100644 index 0000000000000000000000000000000000000000..a1e46a0938d5d962bfe4a296201ac97f99095eef --- /dev/null +++ b/test/fixtures/ban_data_gouv_fr_paris @@ -0,0 +1,117 @@ +{ + "limit": 5, + "attribution": "BAN", + "version": "draft", + "licence": "ODbL 1.0", + "query": "Paris", + "type": "FeatureCollection", + "features": [ + { + "geometry": { + "type": "Point", + "coordinates": [ + 2.3469, + 48.8589 + ] + }, + "properties": { + "adm_weight": "6", + "citycode": "75056", + "name": "Paris", + "city": "Paris", + "postcode": "75000", + "context": "75, Île-de-France", + "score": 1, + "label": "Paris", + "id": "75056", + "type": "city", + "population": "2244" + }, + "type": "Feature" + }, + { + "geometry": { + "type": "Point", + "coordinates": [ + 3.564293, + 45.766413 + ] + }, + "properties": { + "citycode": "63125", + "postcode": "63120", + "name": "Paris", + "city": "Courpière", + "context": "63, Puy-de-Dôme, Auvergne", + "score": 0.8255363636363636, + "label": "Paris 63120 Courpière", + "id": "63125_B221_03549b", + "type": "locality" + }, + "type": "Feature" + }, + { + "geometry": { + "type": "Point", + "coordinates": [ + 1.550208, + 44.673592 + ] + }, + "properties": { + "citycode": "46138", + "postcode": "46240", + "name": "PARIS (Vaillac)", + "city": "Cœur de Causse", + "context": "46, Lot, Midi-Pyrénées", + "score": 0.824090909090909, + "label": "PARIS (Vaillac) 46240 Cœur de Causse", + "id": "46138_XXXX_6ee4ec", + "type": "street" + }, + "type": "Feature" + }, + { + "geometry": { + "type": "Point", + "coordinates": [ + -0.526884, + 43.762253 + ] + }, + "properties": { + "citycode": "40282", + "postcode": "40500", + "name": "Paris", + "city": "Saint-Sever", + "context": "40, Landes, Aquitaine", + "score": 0.8236181818181818, + "label": "Paris 40500 Saint-Sever", + "id": "40282_B237_2364e3", + "type": "locality" + }, + "type": "Feature" + }, + { + "geometry": { + "type": "Point", + "coordinates": [ + 0.157613, + 47.336685 + ] + }, + "properties": { + "citycode": "37031", + "postcode": "37140", + "name": "Paris Buton", + "city": "Bourgueil", + "context": "37, Indre-et-Loire, Centre Val-de-Loire", + "score": 0.8235454545454545, + "label": "Paris Buton 37140 Bourgueil", + "id": "37031_B165_0a5e7a", + "type": "locality" + }, + "type": "Feature" + } + ] +} diff --git a/test/fixtures/ban_data_gouv_fr_reverse b/test/fixtures/ban_data_gouv_fr_reverse new file mode 100644 index 0000000000000000000000000000000000000000..b3a1b680c158f24762c42b3ecaf2c6cbe8ba475a --- /dev/null +++ b/test/fixtures/ban_data_gouv_fr_reverse @@ -0,0 +1,33 @@ +{ + "limit": 1, + "attribution": "BAN", + "version": "draft", + "licence": "ODbL 1.0", + "type": "FeatureCollection", + "features": [ + { + "geometry": { + "type": "Point", + "coordinates": [ + 2.364375, + 48.770639 + ] + }, + "properties": { + "street": "Rue du Lieutenant Alain le Coz", + "label": "4 Rue du Lieutenant Alain le Coz 94550 Chevilly-Larue", + "distance": 23, + "context": "94, Val-de-Marne, Île-de-France", + "id": "94021_1133_49638b", + "citycode": "94021", + "name": "4 Rue du Lieutenant Alain le Coz", + "city": "Chevilly-Larue", + "postcode": "94550", + "housenumber": "4", + "score": 0.9999997696809948, + "type": "housenumber" + }, + "type": "Feature" + } + ] +} diff --git a/test/fixtures/ban_data_gouv_fr_rue_yves_toudic b/test/fixtures/ban_data_gouv_fr_rue_yves_toudic new file mode 100644 index 0000000000000000000000000000000000000000..dea2d42c1d8cc95517bf0007faed42e551da4249 --- /dev/null +++ b/test/fixtures/ban_data_gouv_fr_rue_yves_toudic @@ -0,0 +1,33 @@ +{ + "limit": 5, + "attribution": "BAN", + "version": "draft", + "licence": "ODbL 1.0", + "query": "13 rue yves toudic 75010 Paris", + "type": "FeatureCollection", + "features": [ + { + "geometry": { + "type": "Point", + "coordinates": [ + 2.363473, + 48.870131 + ] + }, + "properties": { + "citycode": "75110", + "postcode": "75010", + "name": "13 Rue Yves Toudic", + "housenumber": "13", + "city": "Paris", + "context": "75, Île-de-France", + "score": 0.9437454545454544, + "label": "13 Rue Yves Toudic 75010 Paris", + "id": "ADRNIVX_0000000270748760", + "type": "housenumber", + "street": "Rue Yves Toudic" + }, + "type": "Feature" + } + ] +} diff --git a/test/test_helper.rb b/test/test_helper.rb index 54a580dc3b7df20df85412da9bb204d51e8b78e6..f2e6f275005a7d8cab6a55f84fb0f50d9d4ef39e 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -361,6 +361,19 @@ module Geocoder end end + require 'geocoder/lookups/ban_data_gouv_fr' + class BanDataGouvFr + private + def fixture_prefix + "ban_data_gouv_fr" + end + + def default_fixture_filename + "#{fixture_prefix}_rue_yves_toudic" + end + end + + end end diff --git a/test/unit/lookups/ban_data_gouv_fr_test.rb b/test/unit/lookups/ban_data_gouv_fr_test.rb new file mode 100644 index 0000000000000000000000000000000000000000..8229c9882b417ffc7e8dcea261724bf7a94a7e45 --- /dev/null +++ b/test/unit/lookups/ban_data_gouv_fr_test.rb @@ -0,0 +1,135 @@ +# encoding: utf-8 +require 'test_helper' + +class BanDataGouvFrTest < GeocoderTestCase + + def setup + Geocoder.configure(lookup: :ban_data_gouv_fr) + end + + def test_query_for_geocode + query = Geocoder::Query.new('13 rue yves toudic, 75010 Paris') + lookup = Geocoder::Lookup.get(:ban_data_gouv_fr) + res = lookup.query_url(query) + assert_equal 'https://api-adresse.data.gouv.fr/search/?q=13+rue+yves+toudic%2C+75010+Paris', res + end + + def test_query_for_reverse_geocode + query = Geocoder::Query.new([48.770639, 2.364375]) + lookup = Geocoder::Lookup.get(:ban_data_gouv_fr) + res = lookup.query_url(query) + assert_equal 'https://api-adresse.data.gouv.fr/reverse/?lat=48.770639&lon=2.364375', res + end + + def test_results_component + result = Geocoder.search('13 rue yves toudic, 75010 Paris').first + assert_equal 'ADRNIVX_0000000270748760', result.location_id + assert_equal 'housenumber', result.result_type + assert_equal 'Paris', result.city_name + assert_equal '13 Rue Yves Toudic 75010 Paris, France', result.international_address + assert_equal '13 Rue Yves Toudic 75010 Paris, France', result.address + assert_equal '13 Rue Yves Toudic 75010 Paris', result.national_address + assert_equal '13 Rue Yves Toudic', result.street_address + assert_equal '13', result.street_number + assert_equal 'Rue Yves Toudic', result.street + assert_equal 'Rue Yves Toudic', result.street_name + assert_equal 'Paris', result.city + assert_equal 'Paris', result.city_name + assert_equal '75110', result.city_code + assert_equal '75010', result.postal_code + assert_equal '75', result.department_code + assert_equal 'Paris', result.department_name + assert_equal 'Île-de-France', result.region_name + assert_equal 'France', result.country + assert_equal 'FR', result.country_code + assert_equal(48.870131, result.coordinates[0]) + assert_equal(2.363473, result.coordinates[1]) + end + + def test_paris_special_business_logic + result = Geocoder.search('paris').first + assert_equal 'city', result.result_type + assert_equal '75000', result.postal_code + assert_equal 'France', result.country + assert_equal 'FR', result.country_code + assert_equal(2244000, result.population) + assert_equal 'Paris', result.city + assert_equal 'Paris', result.city_name + assert_equal '75056', result.city_code + assert_equal '75000', result.postal_code + assert_equal '75', result.department_code + assert_equal 'Paris', result.department_name + assert_equal 'Île-de-France', result.region_name + assert_equal(48.8589, result.coordinates[0]) + assert_equal(2.3469, result.coordinates[1]) + end + + def test_city_result_methods + result = Geocoder.search('montpellier').first + assert_equal 'city', result.result_type + assert_equal '34080', result.postal_code + assert_equal '34172', result.city_code + assert_equal 'France', result.country + assert_equal 'FR', result.country_code + assert_equal(5, result.administrative_weight) + assert_equal(255100, result.population) + assert_equal '34', result.department_code + assert_equal 'Hérault', result.department_name + assert_equal 'Languedoc-Roussillon', result.region_name + assert_equal(43.611024, result.coordinates[0]) + assert_equal(3.875521, result.coordinates[1]) + end + + def test_results_component_when_reverse_geocoding + result = Geocoder.search([48.770431, 2.364463]).first + assert_equal '94021_1133_49638b', result.location_id + assert_equal 'housenumber', result.result_type + assert_equal '4 Rue du Lieutenant Alain le Coz 94550 Chevilly-Larue, France', result.international_address + assert_equal '4 Rue du Lieutenant Alain le Coz 94550 Chevilly-Larue, France', result.address + assert_equal '4 Rue du Lieutenant Alain le Coz 94550 Chevilly-Larue', result.national_address + assert_equal '4 Rue du Lieutenant Alain le Coz', result.street_address + assert_equal '4', result.street_number + assert_equal 'Rue du Lieutenant Alain le Coz', result.street + assert_equal 'Rue du Lieutenant Alain le Coz', result.street_name + assert_equal 'Chevilly-Larue', result.city + assert_equal 'Chevilly-Larue', result.city_name + assert_equal '94021', result.city_code + assert_equal '94550', result.postal_code + assert_equal '94', result.department_code + assert_equal 'Val-de-Marne', result.department_name + assert_equal 'Île-de-France', result.region_name + assert_equal 'France', result.country + assert_equal 'FR', result.country_code + assert_equal(48.770639, result.coordinates[0]) + assert_equal(2.364375, result.coordinates[1]) + end + + def test_no_reverse_results + result = Geocoder.search('no reverse results') + assert_equal 0, result.length + end + + def test_actual_make_api_request_with_https + Geocoder.configure(use_https: true, lookup: :ban_data_gouv_fr) + + require 'webmock/test_unit' + WebMock.enable! + stub_all = WebMock.stub_request(:any, /.*/).to_return(status: 200) + + g = Geocoder::Lookup::BanDataGouvFr.new + g.send(:actual_make_api_request, Geocoder::Query.new('test location')) + assert_requested(stub_all) + + WebMock.reset! + WebMock.disable! + end + + + private + + def assert_country_code(result) + [:state_code, :country_code, :province_code].each do |method| + assert_equal 'FR', result.send(method) + end + end +end