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", fields: [:first_name, :last_name, :email]}) |> 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", fields: [:first_name, :last_name, :email]}) |> 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 end