defmodule Mv.Membership.MemberFuzzySearchLinkingTest do @moduledoc """ Tests fuzzy search in Member.available_for_linking action. Verifies PostgreSQL trigram matching for member search. """ use Mv.DataCase, async: true alias Mv.Accounts alias Mv.Membership describe "available_for_linking with fuzzy search" do test "finds member despite typo" do # Create member with specific name {:ok, member} = Membership.create_member(%{ first_name: "Jonathan", last_name: "Smith", email: "jonathan@example.com" }) # Search with typo query = Mv.Membership.Member |> Ash.Query.for_read(:available_for_linking, %{ user_email: nil, search_query: "Jonatan" }) {:ok, members} = Ash.read(query, domain: Mv.Membership) # Should find Jonathan despite typo assert length(members) == 1 assert hd(members).id == member.id end test "finds member with partial match" do # Create member {:ok, member} = Membership.create_member(%{ first_name: "Alexander", last_name: "Williams", email: "alex@example.com" }) # Search with partial query = Mv.Membership.Member |> Ash.Query.for_read(:available_for_linking, %{ user_email: nil, search_query: "Alex" }) {:ok, members} = Ash.read(query, domain: Mv.Membership) # Should find Alexander assert length(members) == 1 assert hd(members).id == member.id end test "email match overrides fuzzy search" do # Create two members {:ok, member1} = Membership.create_member(%{ first_name: "John", last_name: "Doe", email: "john@example.com" }) {:ok, _member2} = Membership.create_member(%{ first_name: "Jane", last_name: "Smith", email: "jane@example.com" }) # Search with user_email that matches member1, but search_query that would match member2 query = Mv.Membership.Member |> Ash.Query.for_read(:available_for_linking, %{ user_email: "john@example.com", search_query: "Jane" }) {:ok, members} = Ash.read(query, domain: Mv.Membership) # Apply email filter filtered_members = Mv.Membership.Member.filter_by_email_match(members, "john@example.com") # Should only return member1 (email match takes precedence) assert length(filtered_members) == 1 assert hd(filtered_members).id == member1.id end test "limits to 10 results" do # Create 15 members with similar names for i <- 1..15 do Membership.create_member(%{ first_name: "Test#{i}", last_name: "Member", email: "test#{i}@example.com" }) end # Search for "Test" query = Mv.Membership.Member |> Ash.Query.for_read(:available_for_linking, %{ user_email: nil, search_query: "Test" }) {:ok, members} = Ash.read(query, domain: Mv.Membership) # Should return max 10 members assert length(members) == 10 end test "excludes linked members" do # Create member and link to user {:ok, member1} = Membership.create_member(%{ first_name: "Linked", last_name: "Member", email: "linked@example.com" }) {:ok, _user} = Accounts.create_user(%{ email: "user@example.com", member: %{id: member1.id} }) # Create unlinked member {:ok, member2} = Membership.create_member(%{ first_name: "Unlinked", last_name: "Member", email: "unlinked@example.com" }) # Search for "Member" query = Mv.Membership.Member |> Ash.Query.for_read(:available_for_linking, %{ user_email: nil, search_query: "Member" }) {:ok, members} = Ash.read(query, domain: Mv.Membership) # Should only return unlinked member member_ids = Enum.map(members, & &1.id) refute member1.id in member_ids assert member2.id in member_ids end end end