From c72376ac9d0d0f6acdd9b4f26f9318225ef14af5 Mon Sep 17 00:00:00 2001 From: andrew nimmo Date: Wed, 28 Aug 2024 18:15:34 -0700 Subject: [PATCH] Add autocomplete unit tests for exact match --- app/classes/auto_complete/for_clade.rb | 13 +- app/classes/auto_complete/for_region.rb | 13 +- app/classes/auto_complete/for_user.rb | 7 +- app/controllers/autocompleters_controller.rb | 1 + test/models/auto_complete_test.rb | 143 +++++++++++++------ 5 files changed, 115 insertions(+), 62 deletions(-) diff --git a/app/classes/auto_complete/for_clade.rb b/app/classes/auto_complete/for_clade.rb index e4d2924d4c..430a614f46 100644 --- a/app/classes/auto_complete/for_clade.rb +++ b/app/classes/auto_complete/for_clade.rb @@ -10,13 +10,14 @@ def rough_matches(letter) matches_array(clades) end - def exact_match(string) - clade = Name.with_correct_spelling.with_rank_above_genus. - where(Name[:text_name].eq(string)).first - return [] unless clade + # Doesn't make sense to have exact match for clades + # def exact_match(string) + # clade = Name.with_correct_spelling.with_rank_above_genus. + # where(Name[:text_name].eq(string)).first + # return [] unless clade - matches_array([clade]) - end + # matches_array([clade]) + # end # Turn the instances into hashes def matches_array(clades) diff --git a/app/classes/auto_complete/for_region.rb b/app/classes/auto_complete/for_region.rb index b5d96ffa1a..a55888e094 100644 --- a/app/classes/auto_complete/for_region.rb +++ b/app/classes/auto_complete/for_region.rb @@ -19,13 +19,14 @@ def rough_matches(words) matches_array(regions) end - def exact_match(words) - words = Location.reverse_name(words) if reverse - region = Observation.in_region(words).select(:where, :location_id).first - return [] unless region + # Doesn't make sense to have an exact match for a region. + # def exact_match(words) + # words = Location.reverse_name(words) if reverse + # region = Observation.in_region(words).select(:where, :location_id).first + # return [] unless region - matches_array([region]) - end + # matches_array([region]) + # end # Turn the instances into hashes, and alter name order if requested # Also change the names of the hash keys. diff --git a/app/classes/auto_complete/for_user.rb b/app/classes/auto_complete/for_user.rb index 8116fd3b67..bcb39eea73 100644 --- a/app/classes/auto_complete/for_user.rb +++ b/app/classes/auto_complete/for_user.rb @@ -12,9 +12,10 @@ def rough_matches(letter) end def exact_match(string) - user = User.select(:login, :name, :id).distinct. - where(User[:login].eq(string)).or(User[:name].eq(string)). - order(login: :asc).first + user = User.select(:login, :name, :id). + where(User[:login].eq(string)). + or(User.where(User[:name].eq(string))). + order(login: :asc).distinct.first return [] unless user matches_array([user]) diff --git a/app/controllers/autocompleters_controller.rb b/app/controllers/autocompleters_controller.rb index fae6d28bf2..7dc17f96fc 100644 --- a/app/controllers/autocompleters_controller.rb +++ b/app/controllers/autocompleters_controller.rb @@ -38,6 +38,7 @@ def add_context_params end def auto_complete_results + # Don't pass region or clade as the @type with `exact` here. if params[:exact].present? return ::AutoComplete.subclass(@type).new(params).first_matching_record end diff --git a/test/models/auto_complete_test.rb b/test/models/auto_complete_test.rb index 14e1fa9c02..540d7435a5 100644 --- a/test/models/auto_complete_test.rb +++ b/test/models/auto_complete_test.rb @@ -42,6 +42,39 @@ def test_typical_use assert_equal("A", results.first[:name]) assert(results.pluck(:name).include?("Agaricus")) assert(results.pluck(:name).include?("Agaricus campestris")) + + auto = AutoComplete::ForUser.new(string: "Rolf Singer") + results = auto.matching_records + assert(results.pluck(:name).include?("rolf ")) + end + + def test_typical_use_with_exact_match + auto = AutoComplete::ForName.new(string: "Agaricus campestris") + results = auto.first_matching_record + assert_equal("Agaricus campestris", results.first[:name]) + + auto = AutoComplete::ForLocation.new(string: "Gualala, California, USA") + results = auto.first_matching_record + assert_equal("Gualala, California, USA", results.first[:name]) + + auto = AutoComplete::ForSpeciesList.new(string: "Another Species List") + results = auto.first_matching_record + assert_equal("Another Species List", results.first[:name]) + + auto = AutoComplete::ForProject.new(string: "Bolete Project") + results = auto.first_matching_record + assert_equal("Bolete Project", results.first[:name]) + + # Result strings include the code for the herbarium. + auto = AutoComplete::ForHerbarium.new( + string: "The New York Botanical Garden" + ) + results = auto.first_matching_record + assert_equal("NY - The New York Botanical Garden", results.first[:name]) + + auto = AutoComplete::ForUser.new(string: "Rolf Singer") + results = auto.first_matching_record + assert_equal("rolf ", results.first[:name]) end def test_truncate @@ -77,7 +110,57 @@ def test_multiline_matches def test_refine_token_by_string pattern = "one two three" - @list = [ + @list = string_list + string_examples.each do |limit, expected_matches, expected_string| + auto = AutoCompleteMock.new(string: pattern) + auto.matches = @list.sort_by { rand }.map { |str| { name: str, id: 0 } } + auto.limit = limit + assert_refines_correctly(auto, expected_matches, expected_string) + end + end + + def test_refine_token_by_word + pattern = "one two shree" + @list = word_list + word_examples.each do |limit, expected_matches, expected_string| + auto = AutoComplete::ForMock.new(string: pattern) + auto.matches = @list.sort_by { rand }.map { |str| { name: str, id: 0 } } + auto.limit = limit + assert_refines_correctly(auto, expected_matches, expected_string) + end + end + + def assert_refines_correctly(auto, expected_matches, expected_string) + string = auto.refine_token + if string != expected_string || auto.matches.length != expected_matches + msg = "Didn't refine matches correctly for limit = #{auto.limit}:\n" \ + "Refined string: #{string.inspect}, " \ + "expected: #{expected_string.inspect}\n #{show_matches(auto)}" + flunk(msg) + else + pass + end + end + + def show_matches(auto) + result = "" + got = {} + @list.each do |str| + if auto.matches.include?(str) + result += "#{got.length + 1}: #{str.inspect}\n" + got[str] = true + else + result += "X: #{str.inspect}\n" + end + end + auto.matches.each do |str| + result += "UNEXPECTED!! #{str.inspect}\n" unless got[str] + end + result + end + + def string_list + [ "one two three four", # 1 "one two threee", # 2 "one two three", # 3 @@ -89,7 +172,10 @@ def test_refine_token_by_string "o", # 9 "something", # 10 "else" # 11 - ] + ].freeze + end + + def string_examples [ [10, 9, "o"], [9, 9, "o"], @@ -100,17 +186,11 @@ def test_refine_token_by_string [4, 4, "one two t"], [3, 3, "one two th"], [2, 3, "one two three"] - ].each do |limit, expected_matches, expected_string| - auto = AutoCompleteMock.new(string: pattern) - auto.matches = @list.sort_by { rand }.map { |str| { name: str, id: 0 } } - auto.limit = limit - assert_refines_correctly(auto, expected_matches, expected_string) - end + ].freeze end - def test_refine_token_by_word - pattern = "one two shree" - @list = [ + def word_list + [ "one two shree four", # 1 "one two shree" "shreee two one", # 2 "one two shree" "two-shirty-one", # 3 "one two sh" @@ -122,7 +202,10 @@ def test_refine_token_by_word "o", # 9 "o" "something", # 10 "else" # 11 - ] + ].freeze + end + + def word_examples [ [10, 9, "o"], [9, 9, "o"], @@ -134,40 +217,6 @@ def test_refine_token_by_word [3, 3, "one two s"], [2, 2, "one two shr"], [1, 2, "one two shree"] - ].each do |limit, expected_matches, expected_string| - auto = AutoComplete::ForMock.new(string: pattern) - auto.matches = @list.sort_by { rand }.map { |str| { name: str, id: 0 } } - auto.limit = limit - assert_refines_correctly(auto, expected_matches, expected_string) - end - end - - def assert_refines_correctly(auto, expected_matches, expected_string) - string = auto.refine_token - if string != expected_string || auto.matches.length != expected_matches - msg = "Didn't refine matches correctly for limit = #{auto.limit}:\n" \ - "Refined string: #{string.inspect}, " \ - "expected: #{expected_string.inspect}\n #{show_matches(auto)}" - flunk(msg) - else - pass - end - end - - def show_matches(auto) - result = "" - got = {} - @list.each do |str| - if auto.matches.include?(str) - result += "#{got.length + 1}: #{str.inspect}\n" - got[str] = true - else - result += "X: #{str.inspect}\n" - end - end - auto.matches.each do |str| - result += "UNEXPECTED!! #{str.inspect}\n" unless got[str] - end - result + ].freeze end end