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