defmodule Mv.Accounts.UserMemberLinkingEmailTest do @moduledoc """ Tests email validation during user-member linking. Implements rules from docs/email-sync.md. Tests for Issue #168, specifically Problem #4: Email validation bug. """ use Mv.DataCase, async: false alias Mv.Accounts alias Mv.Membership describe "link with same email" do test "succeeds when user.email == member.email" do # Create member with specific email {:ok, member} = Membership.create_member(%{ first_name: "Alice", last_name: "Johnson", email: "alice@example.com" }) # Create user with same email and link to member result = Accounts.create_user(%{ email: "alice@example.com", member: %{id: member.id} }) # Should succeed without errors assert {:ok, user} = result assert to_string(user.email) == "alice@example.com" # Reload to verify link user = Ash.load!(user, [:member], domain: Mv.Accounts) assert user.member.id == member.id assert user.member.email == "alice@example.com" end test "no validation error triggered when updating linked pair with same email" do # Create member {:ok, member} = Membership.create_member(%{ first_name: "Bob", last_name: "Smith", email: "bob@example.com" }) # Create user and link {:ok, user} = Accounts.create_user(%{ email: "bob@example.com", member: %{id: member.id} }) # Update user (should not trigger email validation error) result = Accounts.update_user(user, %{email: "bob@example.com"}) assert {:ok, updated_user} = result assert to_string(updated_user.email) == "bob@example.com" end end describe "link with different emails" do test "fails if member.email is used by a DIFFERENT linked user" do # Create first user and link to a different member {:ok, other_member} = Membership.create_member(%{ first_name: "Other", last_name: "Member", email: "other@example.com" }) {:ok, _user1} = Accounts.create_user(%{ email: "user1@example.com", member: %{id: other_member.id} }) # Reload to ensure email sync happened _other_member = Ash.reload!(other_member) # Create a NEW member with different email {:ok, member} = Membership.create_member(%{ first_name: "Charlie", last_name: "Brown", email: "charlie@example.com" }) # Try to create user2 with email that matches the linked other_member result = Accounts.create_user(%{ email: "user1@example.com", member: %{id: member.id} }) # Should fail because user1@example.com is already used by other_member (which is linked to user1) assert {:error, _error} = result end test "succeeds for unique emails" do # Create member {:ok, member} = Membership.create_member(%{ first_name: "David", last_name: "Wilson", email: "david@example.com" }) # Create user with different but unique email result = Accounts.create_user(%{ email: "user@example.com", member: %{id: member.id} }) # Should succeed assert {:ok, user} = result # Email sync should update member's email to match user's user = Ash.load!(user, [:member], domain: Mv.Accounts) assert user.member.email == "user@example.com" end end describe "edge cases" do test "unlinking and relinking with same email works (Problem #4)" do # This is the exact scenario from Problem #4: # 1. Link user and member (both have same email) # 2. Unlink them (member keeps the email) # 3. Try to relink (validation should NOT fail) # Create member {:ok, member} = Membership.create_member(%{ first_name: "Emma", last_name: "Davis", email: "emma@example.com" }) # Create user and link {:ok, user} = Accounts.create_user(%{ email: "emma@example.com", member: %{id: member.id} }) # Verify they are linked user = Ash.load!(user, [:member], domain: Mv.Accounts) assert user.member.id == member.id assert user.member.email == "emma@example.com" # Unlink {:ok, unlinked_user} = Accounts.update_user(user, %{member: nil}) assert is_nil(unlinked_user.member_id) # Member still has the email after unlink member = Ash.reload!(member) assert member.email == "emma@example.com" # Relink (should work - this is Problem #4) result = Accounts.update_user(unlinked_user, %{member: %{id: member.id}}) assert {:ok, relinked_user} = result assert relinked_user.member_id == member.id end end end