443 lines
11 KiB
Elixir
443 lines
11 KiB
Elixir
defmodule Mv.Membership.FuzzySearchTest do
|
|
use Mv.DataCase, async: false
|
|
|
|
test "fuzzy_search/2 function exists" do
|
|
assert function_exported?(Mv.Membership.Member, :fuzzy_search, 2)
|
|
end
|
|
|
|
test "fuzzy_search returns only John Doe by fuzzy query 'john'" do
|
|
{:ok, john} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "John",
|
|
last_name: "Doe",
|
|
email: "john.doe@example.com"
|
|
})
|
|
|
|
{:ok, _jane} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Adriana",
|
|
last_name: "Smith",
|
|
email: "adriana.smith@example.com"
|
|
})
|
|
|
|
{:ok, alice} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Alice",
|
|
last_name: "Johnson",
|
|
email: "alice.johnson@example.com"
|
|
})
|
|
|
|
result =
|
|
Mv.Membership.Member
|
|
|> Mv.Membership.Member.fuzzy_search(%{
|
|
query: "john"
|
|
})
|
|
|> Ash.read!()
|
|
|
|
assert Enum.map(result, & &1.id) == [john.id, alice.id]
|
|
end
|
|
|
|
test "fuzzy_search finds 'Thomas' when searching misspelled 'tomas'" do
|
|
{:ok, thomas} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Thomas",
|
|
last_name: "Doe",
|
|
email: "john.doe@example.com"
|
|
})
|
|
|
|
{:ok, jane} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Jane",
|
|
last_name: "Smith",
|
|
email: "jane.smith@example.com"
|
|
})
|
|
|
|
{:ok, _alice} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Alice",
|
|
last_name: "Johnson",
|
|
email: "alice.johnson@example.com"
|
|
})
|
|
|
|
result =
|
|
Mv.Membership.Member
|
|
|> Mv.Membership.Member.fuzzy_search(%{
|
|
query: "tomas"
|
|
})
|
|
|> Ash.read!()
|
|
|
|
ids = Enum.map(result, & &1.id)
|
|
assert thomas.id in ids
|
|
refute jane.id in ids
|
|
assert length(ids) >= 1
|
|
end
|
|
|
|
test "empty query returns all members" do
|
|
{:ok, a} =
|
|
Mv.Membership.create_member(%{first_name: "A", last_name: "One", email: "a1@example.com"})
|
|
|
|
{:ok, b} =
|
|
Mv.Membership.create_member(%{first_name: "B", last_name: "Two", email: "b2@example.com"})
|
|
|
|
result =
|
|
Mv.Membership.Member
|
|
|> Mv.Membership.Member.fuzzy_search(%{query: ""})
|
|
|> Ash.read!()
|
|
|
|
assert Enum.sort(Enum.map(result, & &1.id))
|
|
|> Enum.uniq()
|
|
|> Enum.sort()
|
|
|> Enum.all?(fn id -> id in [a.id, b.id] end)
|
|
end
|
|
|
|
test "substring numeric search matches postal_code mid-string" do
|
|
{:ok, m1} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Num",
|
|
last_name: "One",
|
|
email: "n1@example.com",
|
|
postal_code: "12345"
|
|
})
|
|
|
|
{:ok, _m2} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Num",
|
|
last_name: "Two",
|
|
email: "n2@example.com",
|
|
postal_code: "67890"
|
|
})
|
|
|
|
result =
|
|
Mv.Membership.Member
|
|
|> Mv.Membership.Member.fuzzy_search(%{query: "345"})
|
|
|> Ash.read!()
|
|
|
|
ids = Enum.map(result, & &1.id)
|
|
assert m1.id in ids
|
|
end
|
|
|
|
test "substring numeric search matches house_number mid-string" do
|
|
{:ok, m1} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Home",
|
|
last_name: "One",
|
|
email: "h1@example.com",
|
|
house_number: "A345B"
|
|
})
|
|
|
|
{:ok, _m2} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Home",
|
|
last_name: "Two",
|
|
email: "h2@example.com",
|
|
house_number: "77"
|
|
})
|
|
|
|
result =
|
|
Mv.Membership.Member
|
|
|> Mv.Membership.Member.fuzzy_search(%{query: "345"})
|
|
|> Ash.read!()
|
|
|
|
ids = Enum.map(result, & &1.id)
|
|
assert m1.id in ids
|
|
end
|
|
|
|
test "fuzzy matches street misspelling" do
|
|
{:ok, s1} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Road",
|
|
last_name: "Test",
|
|
email: "s1@example.com",
|
|
street: "Main Street"
|
|
})
|
|
|
|
{:ok, _s2} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Road",
|
|
last_name: "Other",
|
|
email: "s2@example.com",
|
|
street: "Second Avenue"
|
|
})
|
|
|
|
result =
|
|
Mv.Membership.Member
|
|
|> Mv.Membership.Member.fuzzy_search(%{query: "mainn"})
|
|
|> Ash.read!()
|
|
|
|
ids = Enum.map(result, & &1.id)
|
|
assert s1.id in ids
|
|
end
|
|
|
|
test "substring in city matches mid-string" do
|
|
{:ok, b} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "City",
|
|
last_name: "One",
|
|
email: "city1@example.com",
|
|
city: "Berlin"
|
|
})
|
|
|
|
{:ok, _m} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "City",
|
|
last_name: "Two",
|
|
email: "city2@example.com",
|
|
city: "München"
|
|
})
|
|
|
|
result =
|
|
Mv.Membership.Member
|
|
|> Mv.Membership.Member.fuzzy_search(%{query: "erl"})
|
|
|> Ash.read!()
|
|
|
|
ids = Enum.map(result, & &1.id)
|
|
assert b.id in ids
|
|
end
|
|
|
|
test "blank character handling: query with spaces matches full name" do
|
|
{:ok, member} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "John",
|
|
last_name: "Doe",
|
|
email: "john.doe@example.com"
|
|
})
|
|
|
|
{:ok, _other} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Jane",
|
|
last_name: "Smith",
|
|
email: "jane.smith@example.com"
|
|
})
|
|
|
|
result =
|
|
Mv.Membership.Member
|
|
|> Mv.Membership.Member.fuzzy_search(%{query: "john doe"})
|
|
|> Ash.read!()
|
|
|
|
ids = Enum.map(result, & &1.id)
|
|
assert member.id in ids
|
|
end
|
|
|
|
test "blank character handling: query with multiple spaces is handled" do
|
|
{:ok, member} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Mary",
|
|
last_name: "Jane",
|
|
email: "mary.jane@example.com"
|
|
})
|
|
|
|
result =
|
|
Mv.Membership.Member
|
|
|> Mv.Membership.Member.fuzzy_search(%{query: "mary jane"})
|
|
|> Ash.read!()
|
|
|
|
ids = Enum.map(result, & &1.id)
|
|
assert member.id in ids
|
|
end
|
|
|
|
test "special character handling: @ symbol in query matches email" do
|
|
{:ok, member} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Test",
|
|
last_name: "User",
|
|
email: "test.user@example.com"
|
|
})
|
|
|
|
{:ok, _other} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Other",
|
|
last_name: "Person",
|
|
email: "other.person@different.org"
|
|
})
|
|
|
|
result =
|
|
Mv.Membership.Member
|
|
|> Mv.Membership.Member.fuzzy_search(%{query: "example"})
|
|
|> Ash.read!()
|
|
|
|
ids = Enum.map(result, & &1.id)
|
|
assert member.id in ids
|
|
end
|
|
|
|
test "special character handling: dot in query matches email" do
|
|
{:ok, member} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Dot",
|
|
last_name: "Test",
|
|
email: "dot.test@example.com"
|
|
})
|
|
|
|
{:ok, _other} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "No",
|
|
last_name: "Dot",
|
|
email: "nodot@example.com"
|
|
})
|
|
|
|
result =
|
|
Mv.Membership.Member
|
|
|> Mv.Membership.Member.fuzzy_search(%{query: "dot.test"})
|
|
|> Ash.read!()
|
|
|
|
ids = Enum.map(result, & &1.id)
|
|
assert member.id in ids
|
|
end
|
|
|
|
test "special character handling: hyphen in query matches data" do
|
|
{:ok, member} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Mary-Jane",
|
|
last_name: "Watson",
|
|
email: "mary.jane@example.com"
|
|
})
|
|
|
|
{:ok, _other} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Mary",
|
|
last_name: "Smith",
|
|
email: "mary.smith@example.com"
|
|
})
|
|
|
|
result =
|
|
Mv.Membership.Member
|
|
|> Mv.Membership.Member.fuzzy_search(%{query: "mary-jane"})
|
|
|> Ash.read!()
|
|
|
|
ids = Enum.map(result, & &1.id)
|
|
assert member.id in ids
|
|
end
|
|
|
|
test "unicode character handling: umlaut ö in query matches data" do
|
|
{:ok, member} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Jörg",
|
|
last_name: "Schmidt",
|
|
email: "joerg.schmidt@example.com"
|
|
})
|
|
|
|
{:ok, _other} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "John",
|
|
last_name: "Smith",
|
|
email: "john.smith@example.com"
|
|
})
|
|
|
|
result =
|
|
Mv.Membership.Member
|
|
|> Mv.Membership.Member.fuzzy_search(%{query: "jörg"})
|
|
|> Ash.read!()
|
|
|
|
ids = Enum.map(result, & &1.id)
|
|
assert member.id in ids
|
|
end
|
|
|
|
test "unicode character handling: umlaut ä in query matches data" do
|
|
{:ok, member} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Märta",
|
|
last_name: "Andersson",
|
|
email: "maerta.andersson@example.com"
|
|
})
|
|
|
|
{:ok, _other} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Marta",
|
|
last_name: "Johnson",
|
|
email: "marta.johnson@example.com"
|
|
})
|
|
|
|
result =
|
|
Mv.Membership.Member
|
|
|> Mv.Membership.Member.fuzzy_search(%{query: "märta"})
|
|
|> Ash.read!()
|
|
|
|
ids = Enum.map(result, & &1.id)
|
|
assert member.id in ids
|
|
end
|
|
|
|
test "unicode character handling: umlaut ü in query matches data" do
|
|
{:ok, member} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Günther",
|
|
last_name: "Müller",
|
|
email: "guenther.mueller@example.com"
|
|
})
|
|
|
|
{:ok, _other} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Gunter",
|
|
last_name: "Miller",
|
|
email: "gunter.miller@example.com"
|
|
})
|
|
|
|
result =
|
|
Mv.Membership.Member
|
|
|> Mv.Membership.Member.fuzzy_search(%{query: "müller"})
|
|
|> Ash.read!()
|
|
|
|
ids = Enum.map(result, & &1.id)
|
|
assert member.id in ids
|
|
end
|
|
|
|
test "unicode character handling: query without umlaut matches data with umlaut" do
|
|
{:ok, member} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Müller",
|
|
last_name: "Schmidt",
|
|
email: "mueller.schmidt@example.com"
|
|
})
|
|
|
|
{:ok, _other} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Miller",
|
|
last_name: "Smith",
|
|
email: "miller.smith@example.com"
|
|
})
|
|
|
|
result =
|
|
Mv.Membership.Member
|
|
|> Mv.Membership.Member.fuzzy_search(%{query: "muller"})
|
|
|> Ash.read!()
|
|
|
|
ids = Enum.map(result, & &1.id)
|
|
assert member.id in ids
|
|
end
|
|
|
|
test "very long search strings: handles long query without error" do
|
|
{:ok, _member} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Test",
|
|
last_name: "User",
|
|
email: "test@example.com"
|
|
})
|
|
|
|
long_query = String.duplicate("a", 1000)
|
|
|
|
result =
|
|
Mv.Membership.Member
|
|
|> Mv.Membership.Member.fuzzy_search(%{query: long_query})
|
|
|> Ash.read!()
|
|
|
|
# Should not crash, may return empty or some results
|
|
assert is_list(result)
|
|
end
|
|
|
|
test "very long search strings: handles extremely long query" do
|
|
{:ok, _member} =
|
|
Mv.Membership.create_member(%{
|
|
first_name: "Test",
|
|
last_name: "User",
|
|
email: "test@example.com"
|
|
})
|
|
|
|
very_long_query = String.duplicate("test query ", 1000)
|
|
|
|
result =
|
|
Mv.Membership.Member
|
|
|> Mv.Membership.Member.fuzzy_search(%{query: very_long_query})
|
|
|> Ash.read!()
|
|
|
|
# Should not crash, may return empty or some results
|
|
assert is_list(result)
|
|
end
|
|
end
|