158 lines
4.2 KiB
Elixir
158 lines
4.2 KiB
Elixir
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
|