Add actor parameter to all tests requiring authorization
This commit adds actor: system_actor to all Ash operations in tests that require authorization.
This commit is contained in:
parent
686f69c9e9
commit
0f48a9b15a
75 changed files with 4686 additions and 2859 deletions
|
|
@ -7,6 +7,11 @@ defmodule Mv.Accounts.EmailSyncEdgeCasesTest do
|
|||
alias Mv.Accounts
|
||||
alias Mv.Membership
|
||||
|
||||
setup do
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
%{actor: system_actor}
|
||||
end
|
||||
|
||||
describe "Email sync edge cases" do
|
||||
@valid_user_attrs %{
|
||||
email: "user@example.com"
|
||||
|
|
@ -18,15 +23,15 @@ defmodule Mv.Accounts.EmailSyncEdgeCasesTest do
|
|||
email: "member@example.com"
|
||||
}
|
||||
|
||||
test "simultaneous email updates use user email as source of truth" do
|
||||
test "simultaneous email updates use user email as source of truth", %{actor: actor} do
|
||||
# Create linked user and member
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs, actor: actor)
|
||||
|
||||
{:ok, user} =
|
||||
Accounts.create_user(Map.put(@valid_user_attrs, :member, %{id: member.id}))
|
||||
Accounts.create_user(Map.put(@valid_user_attrs, :member, %{id: member.id}), actor: actor)
|
||||
|
||||
# Verify link and initial sync
|
||||
{:ok, synced_member} = Ash.get(Mv.Membership.Member, member.id)
|
||||
{:ok, synced_member} = Ash.get(Mv.Membership.Member, member.id, actor: actor)
|
||||
assert synced_member.email == "user@example.com"
|
||||
|
||||
# Scenario: Both emails are updated "simultaneously"
|
||||
|
|
@ -35,58 +40,60 @@ defmodule Mv.Accounts.EmailSyncEdgeCasesTest do
|
|||
|
||||
# Update member email first
|
||||
{:ok, _updated_member} =
|
||||
Membership.update_member(member, %{email: "member-new@example.com"})
|
||||
Membership.update_member(member, %{email: "member-new@example.com"}, actor: actor)
|
||||
|
||||
# Verify it synced to user
|
||||
{:ok, user_after_member_update} = Ash.get(Mv.Accounts.User, user.id)
|
||||
{:ok, user_after_member_update} = Ash.get(Mv.Accounts.User, user.id, actor: actor)
|
||||
assert to_string(user_after_member_update.email) == "member-new@example.com"
|
||||
|
||||
# Now update user email - this should override
|
||||
{:ok, _updated_user} =
|
||||
Accounts.update_user(user_after_member_update, %{email: "user-final@example.com"})
|
||||
Accounts.update_user(user_after_member_update, %{email: "user-final@example.com"},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Reload both
|
||||
{:ok, final_user} = Ash.get(Mv.Accounts.User, user.id)
|
||||
{:ok, final_member} = Ash.get(Mv.Membership.Member, member.id)
|
||||
{:ok, final_user} = Ash.get(Mv.Accounts.User, user.id, actor: actor)
|
||||
{:ok, final_member} = Ash.get(Mv.Membership.Member, member.id, actor: actor)
|
||||
|
||||
# User email should be the final truth
|
||||
assert to_string(final_user.email) == "user-final@example.com"
|
||||
assert final_member.email == "user-final@example.com"
|
||||
end
|
||||
|
||||
test "email validation works for both user and member" do
|
||||
test "email validation works for both user and member", %{actor: actor} do
|
||||
# Test that invalid emails are rejected for both resources
|
||||
|
||||
# Invalid email for user
|
||||
invalid_user_result = Accounts.create_user(%{email: "not-an-email"})
|
||||
invalid_user_result = Accounts.create_user(%{email: "not-an-email"}, actor: actor)
|
||||
assert {:error, %Ash.Error.Invalid{}} = invalid_user_result
|
||||
|
||||
# Invalid email for member
|
||||
invalid_member_attrs = Map.put(@valid_member_attrs, :email, "also-not-an-email")
|
||||
invalid_member_result = Membership.create_member(invalid_member_attrs)
|
||||
invalid_member_result = Membership.create_member(invalid_member_attrs, actor: actor)
|
||||
assert {:error, %Ash.Error.Invalid{}} = invalid_member_result
|
||||
|
||||
# Valid emails should work
|
||||
{:ok, _user} = Accounts.create_user(@valid_user_attrs)
|
||||
{:ok, _member} = Membership.create_member(@valid_member_attrs)
|
||||
{:ok, _user} = Accounts.create_user(@valid_user_attrs, actor: actor)
|
||||
{:ok, _member} = Membership.create_member(@valid_member_attrs, actor: actor)
|
||||
end
|
||||
|
||||
test "identity constraints prevent duplicate emails" do
|
||||
test "identity constraints prevent duplicate emails", %{actor: actor} do
|
||||
# Create first user with an email
|
||||
{:ok, user1} = Accounts.create_user(%{email: "duplicate@example.com"})
|
||||
{:ok, user1} = Accounts.create_user(%{email: "duplicate@example.com"}, actor: actor)
|
||||
assert to_string(user1.email) == "duplicate@example.com"
|
||||
|
||||
# Try to create second user with same email - should fail due to unique constraint
|
||||
result = Accounts.create_user(%{email: "duplicate@example.com"})
|
||||
result = Accounts.create_user(%{email: "duplicate@example.com"}, actor: actor)
|
||||
assert {:error, %Ash.Error.Invalid{}} = result
|
||||
|
||||
# Same for members
|
||||
member_attrs = Map.put(@valid_member_attrs, :email, "member-dup@example.com")
|
||||
{:ok, member1} = Membership.create_member(member_attrs)
|
||||
{:ok, member1} = Membership.create_member(member_attrs, actor: actor)
|
||||
assert member1.email == "member-dup@example.com"
|
||||
|
||||
# Try to create second member with same email - should fail
|
||||
result2 = Membership.create_member(member_attrs)
|
||||
result2 = Membership.create_member(member_attrs, actor: actor)
|
||||
assert {:error, %Ash.Error.Invalid{}} = result2
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,121 +4,177 @@ defmodule Mv.Accounts.EmailUniquenessTest do
|
|||
alias Mv.Accounts
|
||||
alias Mv.Membership
|
||||
|
||||
setup do
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
%{actor: system_actor}
|
||||
end
|
||||
|
||||
describe "Email uniqueness validation - Creation" do
|
||||
test "CAN create member with existing unlinked user email" do
|
||||
test "CAN create member with existing unlinked user email", %{actor: actor} do
|
||||
# Create a user with email
|
||||
{:ok, _user} =
|
||||
Accounts.create_user(%{
|
||||
email: "existing@example.com"
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "existing@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Create member with same email - should succeed
|
||||
{:ok, member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "existing@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "existing@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
assert to_string(member.email) == "existing@example.com"
|
||||
end
|
||||
|
||||
test "CAN create user with existing unlinked member email" do
|
||||
test "CAN create user with existing unlinked member email", %{actor: actor} do
|
||||
# Create a member with email
|
||||
{:ok, _member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "existing@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "existing@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Create user with same email - should succeed
|
||||
{:ok, user} =
|
||||
Accounts.create_user(%{
|
||||
email: "existing@example.com"
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "existing@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
assert to_string(user.email) == "existing@example.com"
|
||||
end
|
||||
end
|
||||
|
||||
describe "Email uniqueness validation - Updating unlinked entities" do
|
||||
test "unlinked member email CAN be changed to an existing unlinked user email" do
|
||||
test "unlinked member email CAN be changed to an existing unlinked user email", %{
|
||||
actor: actor
|
||||
} do
|
||||
# Create a user with email
|
||||
{:ok, _user} =
|
||||
Accounts.create_user(%{
|
||||
email: "existing_user@example.com"
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "existing_user@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Create an unlinked member with different email
|
||||
{:ok, member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "member@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "member@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Change member email to existing user email - should succeed (member is unlinked)
|
||||
{:ok, updated_member} =
|
||||
Membership.update_member(member, %{
|
||||
email: "existing_user@example.com"
|
||||
})
|
||||
Membership.update_member(
|
||||
member,
|
||||
%{
|
||||
email: "existing_user@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
assert to_string(updated_member.email) == "existing_user@example.com"
|
||||
end
|
||||
|
||||
test "unlinked user email CAN be changed to an existing unlinked member email" do
|
||||
test "unlinked user email CAN be changed to an existing unlinked member email", %{
|
||||
actor: actor
|
||||
} do
|
||||
# Create a member with email
|
||||
{:ok, _member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "existing_member@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "existing_member@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Create an unlinked user with different email
|
||||
{:ok, user} =
|
||||
Accounts.create_user(%{
|
||||
email: "user@example.com"
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "user@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Change user email to existing member email - should succeed (user is unlinked)
|
||||
{:ok, updated_user} =
|
||||
Accounts.update_user(user, %{
|
||||
email: "existing_member@example.com"
|
||||
})
|
||||
Accounts.update_user(
|
||||
user,
|
||||
%{
|
||||
email: "existing_member@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
assert to_string(updated_user.email) == "existing_member@example.com"
|
||||
end
|
||||
|
||||
test "unlinked member email CANNOT be changed to an existing linked user email" do
|
||||
test "unlinked member email CANNOT be changed to an existing linked user email", %{
|
||||
actor: actor
|
||||
} do
|
||||
# Create a user and link it to a member - this makes the user "linked"
|
||||
{:ok, user} =
|
||||
Accounts.create_user(%{
|
||||
email: "linked_user@example.com"
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "linked_user@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, _member_a} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Member",
|
||||
last_name: "A",
|
||||
email: "temp@example.com",
|
||||
user: %{id: user.id}
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "Member",
|
||||
last_name: "A",
|
||||
email: "temp@example.com",
|
||||
user: %{id: user.id}
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Create an unlinked member with different email
|
||||
{:ok, member_b} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Member",
|
||||
last_name: "B",
|
||||
email: "member_b@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "Member",
|
||||
last_name: "B",
|
||||
email: "member_b@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Try to change unlinked member's email to linked user's email - should fail
|
||||
result =
|
||||
Membership.update_member(member_b, %{
|
||||
email: "linked_user@example.com"
|
||||
})
|
||||
Membership.update_member(
|
||||
member_b,
|
||||
%{
|
||||
email: "linked_user@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
assert {:error, %Ash.Error.Invalid{} = error} = result
|
||||
|
||||
|
|
@ -129,37 +185,52 @@ defmodule Mv.Accounts.EmailUniquenessTest do
|
|||
end)
|
||||
end
|
||||
|
||||
test "unlinked user email CANNOT be changed to an existing linked member email" do
|
||||
test "unlinked user email CANNOT be changed to an existing linked member email", %{
|
||||
actor: actor
|
||||
} do
|
||||
# Create a user and link it to a member - this makes the member "linked"
|
||||
{:ok, user_a} =
|
||||
Accounts.create_user(%{
|
||||
email: "user_a@example.com"
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "user_a@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, _member_a} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Member",
|
||||
last_name: "A",
|
||||
email: "temp@example.com",
|
||||
user: %{id: user_a.id}
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "Member",
|
||||
last_name: "A",
|
||||
email: "temp@example.com",
|
||||
user: %{id: user_a.id}
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Reload user to get updated member_id and linked member email
|
||||
{:ok, user_a_reloaded} = Ash.get(Mv.Accounts.User, user_a.id)
|
||||
{:ok, user_a_with_member} = Ash.load(user_a_reloaded, :member)
|
||||
{:ok, user_a_reloaded} = Ash.get(Mv.Accounts.User, user_a.id, actor: actor)
|
||||
{:ok, user_a_with_member} = Ash.load(user_a_reloaded, :member, actor: actor)
|
||||
linked_member_email = to_string(user_a_with_member.member.email)
|
||||
|
||||
# Create an unlinked user with different email
|
||||
{:ok, user_b} =
|
||||
Accounts.create_user(%{
|
||||
email: "user_b@example.com"
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "user_b@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Try to change unlinked user's email to linked member's email - should fail
|
||||
result =
|
||||
Accounts.update_user(user_b, %{
|
||||
email: linked_member_email
|
||||
})
|
||||
Accounts.update_user(
|
||||
user_b,
|
||||
%{
|
||||
email: linked_member_email
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
assert {:error, %Ash.Error.Invalid{} = error} = result
|
||||
|
||||
|
|
@ -172,28 +243,37 @@ defmodule Mv.Accounts.EmailUniquenessTest do
|
|||
end
|
||||
|
||||
describe "Email uniqueness validation - Creating with linked emails" do
|
||||
test "CANNOT create member with existing linked user email" do
|
||||
test "CANNOT create member with existing linked user email", %{actor: actor} do
|
||||
# Create a user and link it to a member
|
||||
{:ok, user} =
|
||||
Accounts.create_user(%{
|
||||
email: "linked@example.com"
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "linked@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, _member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "First",
|
||||
last_name: "Member",
|
||||
email: "temp@example.com",
|
||||
user: %{id: user.id}
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "First",
|
||||
last_name: "Member",
|
||||
email: "temp@example.com",
|
||||
user: %{id: user.id}
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Try to create a new member with the linked user's email - should fail
|
||||
result =
|
||||
Membership.create_member(%{
|
||||
first_name: "Second",
|
||||
last_name: "Member",
|
||||
email: "linked@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "Second",
|
||||
last_name: "Member",
|
||||
email: "linked@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
assert {:error, %Ash.Error.Invalid{} = error} = result
|
||||
|
||||
|
|
@ -204,31 +284,40 @@ defmodule Mv.Accounts.EmailUniquenessTest do
|
|||
end)
|
||||
end
|
||||
|
||||
test "CANNOT create user with existing linked member email" do
|
||||
test "CANNOT create user with existing linked member email", %{actor: actor} do
|
||||
# Create a user and link it to a member
|
||||
{:ok, user} =
|
||||
Accounts.create_user(%{
|
||||
email: "user@example.com"
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "user@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, _member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Member",
|
||||
last_name: "One",
|
||||
email: "temp@example.com",
|
||||
user: %{id: user.id}
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "Member",
|
||||
last_name: "One",
|
||||
email: "temp@example.com",
|
||||
user: %{id: user.id}
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Reload user to get the linked member's email
|
||||
{:ok, user_reloaded} = Ash.get(Mv.Accounts.User, user.id)
|
||||
{:ok, user_with_member} = Ash.load(user_reloaded, :member)
|
||||
{:ok, user_reloaded} = Ash.get(Mv.Accounts.User, user.id, actor: actor)
|
||||
{:ok, user_with_member} = Ash.load(user_reloaded, :member, actor: actor)
|
||||
linked_member_email = to_string(user_with_member.member.email)
|
||||
|
||||
# Try to create a new user with the linked member's email - should fail
|
||||
result =
|
||||
Accounts.create_user(%{
|
||||
email: linked_member_email
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: linked_member_email
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
assert {:error, %Ash.Error.Invalid{} = error} = result
|
||||
|
||||
|
|
@ -241,32 +330,45 @@ defmodule Mv.Accounts.EmailUniquenessTest do
|
|||
end
|
||||
|
||||
describe "Email uniqueness validation - Updating linked entities" do
|
||||
test "linked member email CANNOT be changed to an existing user email" do
|
||||
test "linked member email CANNOT be changed to an existing user email", %{actor: actor} do
|
||||
# Create a user with email
|
||||
{:ok, _other_user} =
|
||||
Accounts.create_user(%{
|
||||
email: "other_user@example.com"
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "other_user@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Create a user and link it to a member
|
||||
{:ok, user} =
|
||||
Accounts.create_user(%{
|
||||
email: "user@example.com"
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "user@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "temp@example.com",
|
||||
user: %{id: user.id}
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "temp@example.com",
|
||||
user: %{id: user.id}
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Try to change linked member's email to other user's email - should fail
|
||||
result =
|
||||
Membership.update_member(member, %{
|
||||
email: "other_user@example.com"
|
||||
})
|
||||
Membership.update_member(
|
||||
member,
|
||||
%{
|
||||
email: "other_user@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
assert {:error, %Ash.Error.Invalid{} = error} = result
|
||||
|
||||
|
|
@ -277,37 +379,50 @@ defmodule Mv.Accounts.EmailUniquenessTest do
|
|||
end)
|
||||
end
|
||||
|
||||
test "linked user email CANNOT be changed to an existing member email" do
|
||||
test "linked user email CANNOT be changed to an existing member email", %{actor: actor} do
|
||||
# Create a member with email
|
||||
{:ok, _other_member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Jane",
|
||||
last_name: "Doe",
|
||||
email: "other_member@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "Jane",
|
||||
last_name: "Doe",
|
||||
email: "other_member@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Create a user and link it to a member
|
||||
{:ok, user} =
|
||||
Accounts.create_user(%{
|
||||
email: "user@example.com"
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "user@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, _member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "temp@example.com",
|
||||
user: %{id: user.id}
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "temp@example.com",
|
||||
user: %{id: user.id}
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Reload user to get updated member_id
|
||||
{:ok, user_reloaded} = Ash.get(Mv.Accounts.User, user.id)
|
||||
{:ok, user_reloaded} = Ash.get(Mv.Accounts.User, user.id, actor: actor)
|
||||
|
||||
# Try to change linked user's email to other member's email - should fail
|
||||
result =
|
||||
Accounts.update_user(user_reloaded, %{
|
||||
email: "other_member@example.com"
|
||||
})
|
||||
Accounts.update_user(
|
||||
user_reloaded,
|
||||
%{
|
||||
email: "other_member@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
assert {:error, %Ash.Error.Invalid{} = error} = result
|
||||
|
||||
|
|
@ -320,34 +435,49 @@ defmodule Mv.Accounts.EmailUniquenessTest do
|
|||
end
|
||||
|
||||
describe "Email uniqueness validation - Linking" do
|
||||
test "CANNOT link user to member if user email is already used by another unlinked member" do
|
||||
test "CANNOT link user to member if user email is already used by another unlinked member", %{
|
||||
actor: actor
|
||||
} do
|
||||
# Create a member with email
|
||||
{:ok, _other_member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Jane",
|
||||
last_name: "Doe",
|
||||
email: "duplicate@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "Jane",
|
||||
last_name: "Doe",
|
||||
email: "duplicate@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Create a user with same email
|
||||
{:ok, user} =
|
||||
Accounts.create_user(%{
|
||||
email: "duplicate@example.com"
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "duplicate@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Create a member to link with the user
|
||||
{:ok, member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "John",
|
||||
last_name: "Smith",
|
||||
email: "john@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "John",
|
||||
last_name: "Smith",
|
||||
email: "john@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Try to link user to member - should fail because user.email is already used by other_member
|
||||
result =
|
||||
Accounts.update_user(user, %{
|
||||
member: %{id: member.id}
|
||||
})
|
||||
Accounts.update_user(
|
||||
user,
|
||||
%{
|
||||
member: %{id: member.id}
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
assert {:error, %Ash.Error.Invalid{} = error} = result
|
||||
|
||||
|
|
@ -358,120 +488,160 @@ defmodule Mv.Accounts.EmailUniquenessTest do
|
|||
end)
|
||||
end
|
||||
|
||||
test "CAN link member to user even if member email is used by another user (member email gets overridden)" do
|
||||
test "CAN link member to user even if member email is used by another user (member email gets overridden)",
|
||||
%{actor: actor} do
|
||||
# Create a user with email
|
||||
{:ok, _other_user} =
|
||||
Accounts.create_user(%{
|
||||
email: "duplicate@example.com"
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "duplicate@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Create a member with same email
|
||||
{:ok, member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "duplicate@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "duplicate@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Create a user to link with the member
|
||||
{:ok, user} =
|
||||
Accounts.create_user(%{
|
||||
email: "user@example.com"
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "user@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Link member to user - should succeed because member.email will be overridden
|
||||
{:ok, updated_member} =
|
||||
Membership.update_member(member, %{
|
||||
user: %{id: user.id}
|
||||
})
|
||||
Membership.update_member(
|
||||
member,
|
||||
%{
|
||||
user: %{id: user.id}
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Member email should now be the same as user email
|
||||
{:ok, member_reloaded} = Ash.get(Mv.Membership.Member, updated_member.id)
|
||||
{:ok, member_reloaded} = Ash.get(Mv.Membership.Member, updated_member.id, actor: actor)
|
||||
assert to_string(member_reloaded.email) == "user@example.com"
|
||||
end
|
||||
end
|
||||
|
||||
describe "Email syncing" do
|
||||
test "member email syncs to linked user email without validation error" do
|
||||
test "member email syncs to linked user email without validation error", %{actor: actor} do
|
||||
# Create a user
|
||||
{:ok, user} =
|
||||
Accounts.create_user(%{
|
||||
email: "user@example.com"
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "user@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Create a member linked to this user
|
||||
# The override change will set member.email = user.email automatically
|
||||
{:ok, member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "member@example.com",
|
||||
user: %{id: user.id}
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "member@example.com",
|
||||
user: %{id: user.id}
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Member email should have been overridden to user email
|
||||
# This happens through our sync mechanism, which should NOT trigger
|
||||
# the "email already used" validation because it's the same user
|
||||
{:ok, member_after_link} = Ash.get(Mv.Membership.Member, member.id)
|
||||
{:ok, member_after_link} = Ash.get(Mv.Membership.Member, member.id, actor: actor)
|
||||
assert member_after_link.email == "user@example.com"
|
||||
end
|
||||
|
||||
test "user email syncs to linked member without validation error" do
|
||||
test "user email syncs to linked member without validation error", %{actor: actor} do
|
||||
# Create a member
|
||||
{:ok, member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "member@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "member@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Create a user linked to this member
|
||||
# The override change will set member.email = user.email automatically
|
||||
{:ok, _user} =
|
||||
Accounts.create_user(%{
|
||||
email: "user@example.com",
|
||||
member: %{id: member.id}
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "user@example.com",
|
||||
member: %{id: member.id}
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Member email should have been overridden to user email
|
||||
# This happens through our sync mechanism, which should NOT trigger
|
||||
# the "email already used" validation because it's the same member
|
||||
{:ok, member_after_link} = Ash.get(Mv.Membership.Member, member.id)
|
||||
{:ok, member_after_link} = Ash.get(Mv.Membership.Member, member.id, actor: actor)
|
||||
assert member_after_link.email == "user@example.com"
|
||||
end
|
||||
|
||||
test "two unlinked users cannot have the same email" do
|
||||
test "two unlinked users cannot have the same email", %{actor: actor} do
|
||||
# Create first user
|
||||
{:ok, _user1} =
|
||||
Accounts.create_user(%{
|
||||
email: "duplicate@example.com"
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "duplicate@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Try to create second user with same email
|
||||
result =
|
||||
Accounts.create_user(%{
|
||||
email: "duplicate@example.com"
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "duplicate@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
assert {:error, %Ash.Error.Invalid{}} = result
|
||||
end
|
||||
|
||||
test "two unlinked members cannot have the same email (members have unique constraint)" do
|
||||
test "two unlinked members cannot have the same email (members have unique constraint)", %{
|
||||
actor: actor
|
||||
} do
|
||||
# Create first member
|
||||
{:ok, _member1} =
|
||||
Membership.create_member(%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "duplicate@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "duplicate@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Try to create second member with same email - should fail
|
||||
result =
|
||||
Membership.create_member(%{
|
||||
first_name: "Jane",
|
||||
last_name: "Smith",
|
||||
email: "duplicate@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "Jane",
|
||||
last_name: "Smith",
|
||||
email: "duplicate@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
assert {:error, %Ash.Error.Invalid{}} = result
|
||||
# Members DO have a unique email constraint at database level
|
||||
|
|
|
|||
|
|
@ -10,6 +10,11 @@ defmodule Mv.Accounts.UserAuthenticationTest do
|
|||
use MvWeb.ConnCase, async: true
|
||||
require Ash.Query
|
||||
|
||||
setup do
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
%{actor: system_actor}
|
||||
end
|
||||
|
||||
describe "Password authentication user identification" do
|
||||
@tag :test_proposal
|
||||
test "password login uses email as identifier" do
|
||||
|
|
@ -27,7 +32,7 @@ defmodule Mv.Accounts.UserAuthenticationTest do
|
|||
{:ok, users} =
|
||||
Mv.Accounts.User
|
||||
|> Ash.Query.filter(email == ^email_to_find)
|
||||
|> Ash.read()
|
||||
|> Ash.read(actor: user)
|
||||
|
||||
assert length(users) == 1
|
||||
found_user = List.first(users)
|
||||
|
|
@ -113,11 +118,16 @@ defmodule Mv.Accounts.UserAuthenticationTest do
|
|||
# Use sign_in_with_rauthy to find user by oidc_id
|
||||
# Note: This test will FAIL until we implement the security fix
|
||||
# that changes the filter from email to oidc_id
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
|
||||
result =
|
||||
Mv.Accounts.read_sign_in_with_rauthy(%{
|
||||
user_info: user_info,
|
||||
oauth_tokens: %{}
|
||||
})
|
||||
Mv.Accounts.read_sign_in_with_rauthy(
|
||||
%{
|
||||
user_info: user_info,
|
||||
oauth_tokens: %{}
|
||||
},
|
||||
actor: system_actor
|
||||
)
|
||||
|
||||
case result do
|
||||
{:ok, [found_user]} ->
|
||||
|
|
@ -141,11 +151,16 @@ defmodule Mv.Accounts.UserAuthenticationTest do
|
|||
}
|
||||
|
||||
# Should create via register_with_rauthy
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
|
||||
{:ok, new_user} =
|
||||
Mv.Accounts.create_register_with_rauthy(%{
|
||||
user_info: user_info,
|
||||
oauth_tokens: %{}
|
||||
})
|
||||
Mv.Accounts.create_register_with_rauthy(
|
||||
%{
|
||||
user_info: user_info,
|
||||
oauth_tokens: %{}
|
||||
},
|
||||
actor: system_actor
|
||||
)
|
||||
|
||||
assert to_string(new_user.email) == "newuser@example.com"
|
||||
assert new_user.oidc_id == "brand_new_oidc_789"
|
||||
|
|
@ -170,12 +185,12 @@ defmodule Mv.Accounts.UserAuthenticationTest do
|
|||
{:ok, users1} =
|
||||
Mv.Accounts.User
|
||||
|> Ash.Query.filter(oidc_id == "oidc_unique_1")
|
||||
|> Ash.read()
|
||||
|> Ash.read(actor: user1)
|
||||
|
||||
{:ok, users2} =
|
||||
Mv.Accounts.User
|
||||
|> Ash.Query.filter(oidc_id == "oidc_unique_2")
|
||||
|> Ash.read()
|
||||
|> Ash.read(actor: user2)
|
||||
|
||||
assert length(users1) == 1
|
||||
assert length(users2) == 1
|
||||
|
|
@ -205,11 +220,16 @@ defmodule Mv.Accounts.UserAuthenticationTest do
|
|||
}
|
||||
|
||||
# Should NOT find the user (security requirement)
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
|
||||
result =
|
||||
Mv.Accounts.read_sign_in_with_rauthy(%{
|
||||
user_info: user_info,
|
||||
oauth_tokens: %{}
|
||||
})
|
||||
Mv.Accounts.read_sign_in_with_rauthy(
|
||||
%{
|
||||
user_info: user_info,
|
||||
oauth_tokens: %{}
|
||||
},
|
||||
actor: system_actor
|
||||
)
|
||||
|
||||
# Either returns empty list OR authentication error - both mean "user not found"
|
||||
case result do
|
||||
|
|
@ -241,11 +261,16 @@ defmodule Mv.Accounts.UserAuthenticationTest do
|
|||
}
|
||||
|
||||
# Should NOT find the user because oidc_id is nil
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
|
||||
result =
|
||||
Mv.Accounts.read_sign_in_with_rauthy(%{
|
||||
user_info: user_info,
|
||||
oauth_tokens: %{}
|
||||
})
|
||||
Mv.Accounts.read_sign_in_with_rauthy(
|
||||
%{
|
||||
user_info: user_info,
|
||||
oauth_tokens: %{}
|
||||
},
|
||||
actor: system_actor
|
||||
)
|
||||
|
||||
# Either returns empty list OR authentication error - both mean "user not found"
|
||||
case result do
|
||||
|
|
|
|||
|
|
@ -8,6 +8,11 @@ defmodule Mv.Accounts.UserEmailSyncTest do
|
|||
alias Mv.Accounts
|
||||
alias Mv.Membership
|
||||
|
||||
setup do
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
%{actor: system_actor}
|
||||
end
|
||||
|
||||
describe "User email synchronization to linked Member" do
|
||||
@valid_user_attrs %{
|
||||
email: "user@example.com"
|
||||
|
|
@ -19,96 +24,100 @@ defmodule Mv.Accounts.UserEmailSyncTest do
|
|||
email: "member@example.com"
|
||||
}
|
||||
|
||||
test "updating user email syncs to linked member" do
|
||||
test "updating user email syncs to linked member", %{actor: actor} do
|
||||
# Create a member
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs, actor: actor)
|
||||
assert member.email == "member@example.com"
|
||||
|
||||
# Create a user linked to the member
|
||||
{:ok, user} =
|
||||
Accounts.create_user(Map.put(@valid_user_attrs, :member, %{id: member.id}))
|
||||
Accounts.create_user(Map.put(@valid_user_attrs, :member, %{id: member.id}), actor: actor)
|
||||
|
||||
# Verify initial state - member email should be overridden by user email
|
||||
{:ok, member_after_link} = Ash.get(Mv.Membership.Member, member.id)
|
||||
{:ok, member_after_link} = Ash.get(Mv.Membership.Member, member.id, actor: actor)
|
||||
assert member_after_link.email == "user@example.com"
|
||||
|
||||
# Update user email
|
||||
{:ok, updated_user} = Accounts.update_user(user, %{email: "newemail@example.com"})
|
||||
{:ok, updated_user} =
|
||||
Accounts.update_user(user, %{email: "newemail@example.com"}, actor: actor)
|
||||
|
||||
assert to_string(updated_user.email) == "newemail@example.com"
|
||||
|
||||
# Verify member email was also updated
|
||||
{:ok, synced_member} = Ash.get(Mv.Membership.Member, member.id)
|
||||
{:ok, synced_member} = Ash.get(Mv.Membership.Member, member.id, actor: actor)
|
||||
assert synced_member.email == "newemail@example.com"
|
||||
end
|
||||
|
||||
test "creating user linked to member overrides member email" do
|
||||
test "creating user linked to member overrides member email", %{actor: actor} do
|
||||
# Create a member with their own email
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs, actor: actor)
|
||||
assert member.email == "member@example.com"
|
||||
|
||||
# Create a user linked to this member
|
||||
{:ok, user} =
|
||||
Accounts.create_user(Map.put(@valid_user_attrs, :member, %{id: member.id}))
|
||||
Accounts.create_user(Map.put(@valid_user_attrs, :member, %{id: member.id}), actor: actor)
|
||||
|
||||
assert to_string(user.email) == "user@example.com"
|
||||
assert user.member_id == member.id
|
||||
|
||||
# Verify member email was overridden with user email
|
||||
{:ok, updated_member} = Ash.get(Mv.Membership.Member, member.id)
|
||||
{:ok, updated_member} = Ash.get(Mv.Membership.Member, member.id, actor: actor)
|
||||
assert updated_member.email == "user@example.com"
|
||||
end
|
||||
|
||||
test "linking user to existing member syncs user email to member" do
|
||||
test "linking user to existing member syncs user email to member", %{actor: actor} do
|
||||
# Create a standalone member
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs, actor: actor)
|
||||
assert member.email == "member@example.com"
|
||||
|
||||
# Create a standalone user
|
||||
{:ok, user} = Accounts.create_user(@valid_user_attrs)
|
||||
{:ok, user} = Accounts.create_user(@valid_user_attrs, actor: actor)
|
||||
assert to_string(user.email) == "user@example.com"
|
||||
assert user.member_id == nil
|
||||
|
||||
# Link the user to the member
|
||||
{:ok, linked_user} = Accounts.update_user(user, %{member: %{id: member.id}})
|
||||
{:ok, linked_user} = Accounts.update_user(user, %{member: %{id: member.id}}, actor: actor)
|
||||
assert linked_user.member_id == member.id
|
||||
|
||||
# Verify member email was overridden with user email
|
||||
{:ok, synced_member} = Ash.get(Mv.Membership.Member, member.id)
|
||||
{:ok, synced_member} = Ash.get(Mv.Membership.Member, member.id, actor: actor)
|
||||
assert synced_member.email == "user@example.com"
|
||||
end
|
||||
|
||||
test "updating user email when no member linked does not error" do
|
||||
test "updating user email when no member linked does not error", %{actor: actor} do
|
||||
# Create a standalone user without member link
|
||||
{:ok, user} = Accounts.create_user(@valid_user_attrs)
|
||||
{:ok, user} = Accounts.create_user(@valid_user_attrs, actor: actor)
|
||||
assert to_string(user.email) == "user@example.com"
|
||||
assert user.member_id == nil
|
||||
|
||||
# Update user email - should work fine without error
|
||||
{:ok, updated_user} = Accounts.update_user(user, %{email: "newemail@example.com"})
|
||||
{:ok, updated_user} =
|
||||
Accounts.update_user(user, %{email: "newemail@example.com"}, actor: actor)
|
||||
|
||||
assert to_string(updated_user.email) == "newemail@example.com"
|
||||
assert updated_user.member_id == nil
|
||||
end
|
||||
|
||||
test "unlinking user from member does not sync email" do
|
||||
test "unlinking user from member does not sync email", %{actor: actor} do
|
||||
# Create member
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs, actor: actor)
|
||||
|
||||
# Create user linked to member
|
||||
{:ok, user} =
|
||||
Accounts.create_user(Map.put(@valid_user_attrs, :member, %{id: member.id}))
|
||||
Accounts.create_user(Map.put(@valid_user_attrs, :member, %{id: member.id}), actor: actor)
|
||||
|
||||
assert user.member_id == member.id
|
||||
|
||||
# Verify member email was synced
|
||||
{:ok, synced_member} = Ash.get(Mv.Membership.Member, member.id)
|
||||
{:ok, synced_member} = Ash.get(Mv.Membership.Member, member.id, actor: actor)
|
||||
assert synced_member.email == "user@example.com"
|
||||
|
||||
# Unlink user from member
|
||||
{:ok, unlinked_user} = Accounts.update_user(user, %{member: nil})
|
||||
{:ok, unlinked_user} = Accounts.update_user(user, %{member: nil}, actor: actor)
|
||||
assert unlinked_user.member_id == nil
|
||||
|
||||
# Member email should remain unchanged after unlinking
|
||||
{:ok, member_after_unlink} = Ash.get(Mv.Membership.Member, member.id)
|
||||
{:ok, member_after_unlink} = Ash.get(Mv.Membership.Member, member.id, actor: actor)
|
||||
assert member_after_unlink.email == "user@example.com"
|
||||
end
|
||||
end
|
||||
|
|
@ -119,6 +128,8 @@ defmodule Mv.Accounts.UserEmailSyncTest do
|
|||
email = "test@example.com"
|
||||
password = "securepassword123"
|
||||
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
|
||||
# Create user with password strategy (simulating registration)
|
||||
{:ok, user} =
|
||||
Mv.Accounts.User
|
||||
|
|
@ -126,7 +137,7 @@ defmodule Mv.Accounts.UserEmailSyncTest do
|
|||
email: email,
|
||||
password: password
|
||||
})
|
||||
|> Ash.create()
|
||||
|> Ash.create(actor: system_actor)
|
||||
|
||||
assert to_string(user.email) == email
|
||||
assert user.hashed_password != nil
|
||||
|
|
@ -138,7 +149,7 @@ defmodule Mv.Accounts.UserEmailSyncTest do
|
|||
email: email,
|
||||
password: password
|
||||
})
|
||||
|> Ash.read_one()
|
||||
|> Ash.read_one(actor: system_actor)
|
||||
|
||||
assert signed_in_user.id == user.id
|
||||
assert to_string(signed_in_user.email) == email
|
||||
|
|
@ -153,6 +164,8 @@ defmodule Mv.Accounts.UserEmailSyncTest do
|
|||
|
||||
oauth_tokens = %{"access_token" => "mock_token"}
|
||||
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
|
||||
# Simulate OIDC registration
|
||||
{:ok, user} =
|
||||
Mv.Accounts.User
|
||||
|
|
@ -160,7 +173,7 @@ defmodule Mv.Accounts.UserEmailSyncTest do
|
|||
user_info: user_info,
|
||||
oauth_tokens: oauth_tokens
|
||||
})
|
||||
|> Ash.create()
|
||||
|> Ash.create(actor: system_actor)
|
||||
|
||||
assert to_string(user.email) == "oidc@example.com"
|
||||
assert user.oidc_id == "oidc-user-123"
|
||||
|
|
|
|||
|
|
@ -18,71 +18,86 @@ defmodule Mv.Accounts.UserMemberDeletionTest do
|
|||
email: "john@example.com"
|
||||
}
|
||||
|
||||
test "deleting a member sets the user's member_id to NULL" do
|
||||
setup do
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
%{actor: system_actor}
|
||||
end
|
||||
|
||||
test "deleting a member sets the user's member_id to NULL", %{actor: actor} do
|
||||
# Create a member
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs, actor: actor)
|
||||
|
||||
# Create a user linked to the member
|
||||
{:ok, user} =
|
||||
Accounts.create_user(Map.put(@valid_user_attrs, :member, %{id: member.id}))
|
||||
Accounts.create_user(Map.put(@valid_user_attrs, :member, %{id: member.id}), actor: actor)
|
||||
|
||||
# Verify the relationship is established
|
||||
{:ok, user_before_delete} = Ash.get(Mv.Accounts.User, user.id, load: [:member])
|
||||
{:ok, user_before_delete} =
|
||||
Ash.get(Mv.Accounts.User, user.id, actor: actor, load: [:member], actor: actor)
|
||||
|
||||
assert user_before_delete.member_id == member.id
|
||||
assert user_before_delete.member.id == member.id
|
||||
|
||||
# Delete the member
|
||||
:ok = Membership.destroy_member(member)
|
||||
:ok = Membership.destroy_member(member, actor: actor)
|
||||
|
||||
# Verify the user still exists but member_id is NULL
|
||||
{:ok, user_after_delete} = Ash.get(Mv.Accounts.User, user.id, load: [:member])
|
||||
{:ok, user_after_delete} =
|
||||
Ash.get(Mv.Accounts.User, user.id, actor: actor, load: [:member], actor: actor)
|
||||
|
||||
assert user_after_delete.id == user.id
|
||||
assert user_after_delete.member_id == nil
|
||||
assert user_after_delete.member == nil
|
||||
end
|
||||
|
||||
test "user can be linked to a new member after old member is deleted" do
|
||||
test "user can be linked to a new member after old member is deleted", %{actor: actor} do
|
||||
# Create first member
|
||||
{:ok, member1} = Membership.create_member(@valid_member_attrs)
|
||||
{:ok, member1} = Membership.create_member(@valid_member_attrs, actor: actor)
|
||||
|
||||
# Create user linked to first member
|
||||
{:ok, user} =
|
||||
Accounts.create_user(Map.put(@valid_user_attrs, :member, %{id: member1.id}))
|
||||
Accounts.create_user(Map.put(@valid_user_attrs, :member, %{id: member1.id}), actor: actor)
|
||||
|
||||
assert user.member_id == member1.id
|
||||
|
||||
# Delete first member
|
||||
:ok = Membership.destroy_member(member1)
|
||||
:ok = Membership.destroy_member(member1, actor: actor)
|
||||
|
||||
# Reload user from database to get updated member_id (should be NULL)
|
||||
{:ok, user_after_delete} = Ash.get(Mv.Accounts.User, user.id)
|
||||
{:ok, user_after_delete} = Ash.get(Mv.Accounts.User, user.id, actor: actor)
|
||||
assert user_after_delete.member_id == nil
|
||||
|
||||
# Create second member
|
||||
{:ok, member2} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Jane",
|
||||
last_name: "Smith",
|
||||
email: "jane@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "Jane",
|
||||
last_name: "Smith",
|
||||
email: "jane@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Link user to second member (use reloaded user)
|
||||
{:ok, updated_user} = Accounts.update_user(user_after_delete, %{member: %{id: member2.id}})
|
||||
{:ok, updated_user} =
|
||||
Accounts.update_user(user_after_delete, %{member: %{id: member2.id}}, actor: actor)
|
||||
|
||||
# Verify new relationship
|
||||
{:ok, final_user} = Ash.get(Mv.Accounts.User, updated_user.id, load: [:member])
|
||||
{:ok, final_user} =
|
||||
Ash.get(Mv.Accounts.User, updated_user.id, actor: actor, load: [:member])
|
||||
|
||||
assert final_user.member_id == member2.id
|
||||
assert final_user.member.id == member2.id
|
||||
end
|
||||
|
||||
test "member without linked user can be deleted normally" do
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
test "member without linked user can be deleted normally", %{actor: actor} do
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs, actor: actor)
|
||||
|
||||
# Delete member (no users linked)
|
||||
assert :ok = Membership.destroy_member(member)
|
||||
assert :ok = Membership.destroy_member(member, actor: actor)
|
||||
|
||||
# Verify member is deleted
|
||||
assert {:error, _} = Ash.get(Mv.Membership.Member, member.id)
|
||||
assert {:error, _} = Ash.get(Mv.Membership.Member, member.id, actor: actor)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,51 +10,70 @@ defmodule Mv.Accounts.UserMemberLinkingEmailTest do
|
|||
alias Mv.Accounts
|
||||
alias Mv.Membership
|
||||
|
||||
setup do
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
%{actor: system_actor}
|
||||
end
|
||||
|
||||
describe "link with same email" do
|
||||
test "succeeds when user.email == member.email" do
|
||||
test "succeeds when user.email == member.email", %{actor: actor} do
|
||||
# Create member with specific email
|
||||
{:ok, member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Alice",
|
||||
last_name: "Johnson",
|
||||
email: "alice@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "Alice",
|
||||
last_name: "Johnson",
|
||||
email: "alice@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Create user with same email and link to member
|
||||
result =
|
||||
Accounts.create_user(%{
|
||||
email: "alice@example.com",
|
||||
member: %{id: member.id}
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "alice@example.com",
|
||||
member: %{id: member.id}
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# 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)
|
||||
user = Ash.load!(user, [:member], domain: Mv.Accounts, actor: actor)
|
||||
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
|
||||
test "no validation error triggered when updating linked pair with same email", %{
|
||||
actor: actor
|
||||
} do
|
||||
# Create member
|
||||
{:ok, member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Bob",
|
||||
last_name: "Smith",
|
||||
email: "bob@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "Bob",
|
||||
last_name: "Smith",
|
||||
email: "bob@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Create user and link
|
||||
{:ok, user} =
|
||||
Accounts.create_user(%{
|
||||
email: "bob@example.com",
|
||||
member: %{id: member.id}
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "bob@example.com",
|
||||
member: %{id: member.id}
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Update user (should not trigger email validation error)
|
||||
result = Accounts.update_user(user, %{email: "bob@example.com"})
|
||||
result = Accounts.update_user(user, %{email: "bob@example.com"}, actor: actor)
|
||||
|
||||
assert {:ok, updated_user} = result
|
||||
assert to_string(updated_user.email) == "bob@example.com"
|
||||
|
|
@ -62,70 +81,88 @@ defmodule Mv.Accounts.UserMemberLinkingEmailTest do
|
|||
end
|
||||
|
||||
describe "link with different emails" do
|
||||
test "fails if member.email is used by a DIFFERENT linked user" do
|
||||
test "fails if member.email is used by a DIFFERENT linked user", %{actor: actor} 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"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "Other",
|
||||
last_name: "Member",
|
||||
email: "other@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, _user1} =
|
||||
Accounts.create_user(%{
|
||||
email: "user1@example.com",
|
||||
member: %{id: other_member.id}
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "user1@example.com",
|
||||
member: %{id: other_member.id}
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Reload to ensure email sync happened
|
||||
_other_member = Ash.reload!(other_member)
|
||||
_other_member = Ash.reload!(other_member, actor: actor)
|
||||
|
||||
# Create a NEW member with different email
|
||||
{:ok, member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Charlie",
|
||||
last_name: "Brown",
|
||||
email: "charlie@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "Charlie",
|
||||
last_name: "Brown",
|
||||
email: "charlie@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Try to create user2 with email that matches the linked other_member
|
||||
result =
|
||||
Accounts.create_user(%{
|
||||
email: "user1@example.com",
|
||||
member: %{id: member.id}
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "user1@example.com",
|
||||
member: %{id: member.id}
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# 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
|
||||
test "succeeds for unique emails", %{actor: actor} do
|
||||
# Create member
|
||||
{:ok, member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "David",
|
||||
last_name: "Wilson",
|
||||
email: "david@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "David",
|
||||
last_name: "Wilson",
|
||||
email: "david@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Create user with different but unique email
|
||||
result =
|
||||
Accounts.create_user(%{
|
||||
email: "user@example.com",
|
||||
member: %{id: member.id}
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "user@example.com",
|
||||
member: %{id: member.id}
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# 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)
|
||||
user = Ash.load!(user, [:member], domain: Mv.Accounts, actor: actor)
|
||||
assert user.member.email == "user@example.com"
|
||||
end
|
||||
end
|
||||
|
||||
describe "edge cases" do
|
||||
test "unlinking and relinking with same email works (Problem #4)" do
|
||||
test "unlinking and relinking with same email works (Problem #4)", %{actor: actor} 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)
|
||||
|
|
@ -133,34 +170,40 @@ defmodule Mv.Accounts.UserMemberLinkingEmailTest do
|
|||
|
||||
# Create member
|
||||
{:ok, member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Emma",
|
||||
last_name: "Davis",
|
||||
email: "emma@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "Emma",
|
||||
last_name: "Davis",
|
||||
email: "emma@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Create user and link
|
||||
{:ok, user} =
|
||||
Accounts.create_user(%{
|
||||
email: "emma@example.com",
|
||||
member: %{id: member.id}
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "emma@example.com",
|
||||
member: %{id: member.id}
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Verify they are linked
|
||||
user = Ash.load!(user, [:member], domain: Mv.Accounts)
|
||||
user = Ash.load!(user, [:member], domain: Mv.Accounts, actor: actor)
|
||||
assert user.member.id == member.id
|
||||
assert user.member.email == "emma@example.com"
|
||||
|
||||
# Unlink
|
||||
{:ok, unlinked_user} = Accounts.update_user(user, %{member: nil})
|
||||
{:ok, unlinked_user} = Accounts.update_user(user, %{member: nil}, actor: actor)
|
||||
assert is_nil(unlinked_user.member_id)
|
||||
|
||||
# Member still has the email after unlink
|
||||
member = Ash.reload!(member)
|
||||
member = Ash.reload!(member, actor: actor)
|
||||
assert member.email == "emma@example.com"
|
||||
|
||||
# Relink (should work - this is Problem #4)
|
||||
result = Accounts.update_user(unlinked_user, %{member: %{id: member.id}})
|
||||
result = Accounts.update_user(unlinked_user, %{member: %{id: member.id}}, actor: actor)
|
||||
|
||||
assert {:ok, relinked_user} = result
|
||||
assert relinked_user.member_id == member.id
|
||||
|
|
|
|||
|
|
@ -9,121 +9,150 @@ defmodule Mv.Accounts.UserMemberLinkingTest do
|
|||
alias Mv.Accounts
|
||||
alias Mv.Membership
|
||||
|
||||
setup do
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
%{actor: system_actor}
|
||||
end
|
||||
|
||||
describe "User-Member Linking with Email Sync" do
|
||||
test "link user to member with different email syncs member email" do
|
||||
test "link user to member with different email syncs member email", %{actor: actor} do
|
||||
# Create user with one email
|
||||
{:ok, user} = Accounts.create_user(%{email: "user@example.com"})
|
||||
{:ok, user} = Accounts.create_user(%{email: "user@example.com"}, actor: actor)
|
||||
|
||||
# Create member with different email
|
||||
{:ok, member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "member@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
email: "member@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Link user to member
|
||||
{:ok, updated_user} = Accounts.update_user(user, %{member: %{id: member.id}})
|
||||
{:ok, updated_user} = Accounts.update_user(user, %{member: %{id: member.id}}, actor: actor)
|
||||
|
||||
# Verify link exists
|
||||
user_with_member = Ash.get!(Mv.Accounts.User, updated_user.id, load: [:member])
|
||||
user_with_member =
|
||||
Ash.get!(Mv.Accounts.User, updated_user.id, actor: actor, load: [:member])
|
||||
|
||||
assert user_with_member.member.id == member.id
|
||||
|
||||
# Verify member email was synced to match user email
|
||||
synced_member = Ash.get!(Mv.Membership.Member, member.id)
|
||||
synced_member = Ash.get!(Mv.Membership.Member, member.id, actor: actor)
|
||||
assert synced_member.email == "user@example.com"
|
||||
end
|
||||
|
||||
test "unlink member from user sets member to nil" do
|
||||
test "unlink member from user sets member to nil", %{actor: actor} do
|
||||
# Create and link user and member
|
||||
{:ok, user} = Accounts.create_user(%{email: "user@example.com"})
|
||||
{:ok, user} = Accounts.create_user(%{email: "user@example.com"}, actor: actor)
|
||||
|
||||
{:ok, member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Jane",
|
||||
last_name: "Smith",
|
||||
email: "jane@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "Jane",
|
||||
last_name: "Smith",
|
||||
email: "jane@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, linked_user} = Accounts.update_user(user, %{member: %{id: member.id}})
|
||||
{:ok, linked_user} = Accounts.update_user(user, %{member: %{id: member.id}}, actor: actor)
|
||||
|
||||
# Verify link exists
|
||||
user_with_member = Ash.get!(Mv.Accounts.User, linked_user.id, load: [:member])
|
||||
user_with_member = Ash.get!(Mv.Accounts.User, linked_user.id, actor: actor, load: [:member])
|
||||
assert user_with_member.member.id == member.id
|
||||
|
||||
# Unlink by setting member to nil
|
||||
{:ok, unlinked_user} = Accounts.update_user(linked_user, %{member: nil})
|
||||
{:ok, unlinked_user} = Accounts.update_user(linked_user, %{member: nil}, actor: actor)
|
||||
|
||||
# Verify link is removed
|
||||
user_without_member = Ash.get!(Mv.Accounts.User, unlinked_user.id, load: [:member])
|
||||
user_without_member =
|
||||
Ash.get!(Mv.Accounts.User, unlinked_user.id, actor: actor, load: [:member])
|
||||
|
||||
assert is_nil(user_without_member.member)
|
||||
|
||||
# Verify member still exists independently
|
||||
member_still_exists = Ash.get!(Mv.Membership.Member, member.id)
|
||||
member_still_exists = Ash.get!(Mv.Membership.Member, member.id, actor: actor)
|
||||
assert member_still_exists.id == member.id
|
||||
end
|
||||
|
||||
test "cannot link member already linked to another user" do
|
||||
test "cannot link member already linked to another user", %{actor: actor} do
|
||||
# Create first user and link to member
|
||||
{:ok, user1} = Accounts.create_user(%{email: "user1@example.com"})
|
||||
{:ok, user1} = Accounts.create_user(%{email: "user1@example.com"}, actor: actor)
|
||||
|
||||
{:ok, member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Bob",
|
||||
last_name: "Wilson",
|
||||
email: "bob@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "Bob",
|
||||
last_name: "Wilson",
|
||||
email: "bob@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, _linked_user1} = Accounts.update_user(user1, %{member: %{id: member.id}})
|
||||
{:ok, _linked_user1} =
|
||||
Accounts.update_user(user1, %{member: %{id: member.id}}, actor: actor)
|
||||
|
||||
# Create second user and try to link to same member
|
||||
{:ok, user2} = Accounts.create_user(%{email: "user2@example.com"})
|
||||
{:ok, user2} = Accounts.create_user(%{email: "user2@example.com"}, actor: actor)
|
||||
|
||||
# Should fail because member is already linked
|
||||
assert {:error, %Ash.Error.Invalid{}} =
|
||||
Accounts.update_user(user2, %{member: %{id: member.id}})
|
||||
Accounts.update_user(user2, %{member: %{id: member.id}}, actor: actor)
|
||||
end
|
||||
|
||||
test "cannot change member link directly, must unlink first" do
|
||||
test "cannot change member link directly, must unlink first", %{actor: actor} do
|
||||
# Create user and link to first member
|
||||
{:ok, user} = Accounts.create_user(%{email: "user@example.com"})
|
||||
{:ok, user} = Accounts.create_user(%{email: "user@example.com"}, actor: actor)
|
||||
|
||||
{:ok, member1} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Alice",
|
||||
last_name: "Johnson",
|
||||
email: "alice@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "Alice",
|
||||
last_name: "Johnson",
|
||||
email: "alice@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, linked_user} = Accounts.update_user(user, %{member: %{id: member1.id}})
|
||||
{:ok, linked_user} = Accounts.update_user(user, %{member: %{id: member1.id}}, actor: actor)
|
||||
|
||||
# Create second member
|
||||
{:ok, member2} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Charlie",
|
||||
last_name: "Brown",
|
||||
email: "charlie@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "Charlie",
|
||||
last_name: "Brown",
|
||||
email: "charlie@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
# Try to directly change member link (should fail)
|
||||
assert {:error, %Ash.Error.Invalid{errors: errors}} =
|
||||
Accounts.update_user(linked_user, %{member: %{id: member2.id}})
|
||||
Accounts.update_user(linked_user, %{member: %{id: member2.id}}, actor: actor)
|
||||
|
||||
# Verify error message mentions "Remove existing member first"
|
||||
error_messages = Enum.map(errors, & &1.message)
|
||||
assert Enum.any?(error_messages, &String.contains?(&1, "Remove existing member first"))
|
||||
|
||||
# Two-step process: first unlink, then link new member
|
||||
{:ok, unlinked_user} = Accounts.update_user(linked_user, %{member: nil})
|
||||
{:ok, unlinked_user} = Accounts.update_user(linked_user, %{member: nil}, actor: actor)
|
||||
|
||||
# After unlinking, member1 still has the user's email
|
||||
# Change member1's email to avoid conflict when relinking to member2
|
||||
{:ok, _} = Membership.update_member(member1, %{email: "alice_changed@example.com"})
|
||||
{:ok, _} =
|
||||
Membership.update_member(member1, %{email: "alice_changed@example.com"}, actor: actor)
|
||||
|
||||
{:ok, relinked_user} = Accounts.update_user(unlinked_user, %{member: %{id: member2.id}})
|
||||
{:ok, relinked_user} =
|
||||
Accounts.update_user(unlinked_user, %{member: %{id: member2.id}}, actor: actor)
|
||||
|
||||
# Verify new link is established
|
||||
user_with_new_member = Ash.get!(Mv.Accounts.User, relinked_user.id, load: [:member])
|
||||
user_with_new_member =
|
||||
Ash.get!(Mv.Accounts.User, relinked_user.id, actor: actor, load: [:member])
|
||||
|
||||
assert user_with_new_member.member.id == member2.id
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,6 +5,11 @@ defmodule Mv.Accounts.UserMemberRelationshipTest do
|
|||
alias Mv.Accounts
|
||||
alias Mv.Membership
|
||||
|
||||
setup do
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
%{actor: system_actor}
|
||||
end
|
||||
|
||||
describe "User-Member Relationship - Basic Tests" do
|
||||
@valid_user_attrs %{
|
||||
email: "test@example.com"
|
||||
|
|
@ -16,22 +21,26 @@ defmodule Mv.Accounts.UserMemberRelationshipTest do
|
|||
email: "john@example.com"
|
||||
}
|
||||
|
||||
test "user can exist without member" do
|
||||
{:ok, user} = Accounts.create_user(@valid_user_attrs)
|
||||
test "user can exist without member", %{actor: actor} do
|
||||
{:ok, user} = Accounts.create_user(@valid_user_attrs, actor: actor)
|
||||
assert user.member_id == nil
|
||||
|
||||
# Load the relationship to test it
|
||||
{:ok, user_with_member} = Ash.get(Mv.Accounts.User, user.id, load: [:member])
|
||||
{:ok, user_with_member} =
|
||||
Ash.get(Mv.Accounts.User, user.id, actor: actor, load: [:member], actor: actor)
|
||||
|
||||
assert user_with_member.member == nil
|
||||
end
|
||||
|
||||
test "member can exist without user" do
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
test "member can exist without user", %{actor: actor} do
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs, actor: actor)
|
||||
assert member.id != nil
|
||||
assert member.first_name == "John"
|
||||
|
||||
# Load the relationship to test it
|
||||
{:ok, member_with_user} = Ash.get(Mv.Membership.Member, member.id, load: [:user])
|
||||
{:ok, member_with_user} =
|
||||
Ash.get(Mv.Membership.Member, member.id, actor: actor, load: [:user], actor: actor)
|
||||
|
||||
assert member_with_user.user == nil
|
||||
end
|
||||
end
|
||||
|
|
@ -47,47 +56,58 @@ defmodule Mv.Accounts.UserMemberRelationshipTest do
|
|||
email: "alice@example.com"
|
||||
}
|
||||
|
||||
test "user can be linked to member during user creation" do
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
test "user can be linked to member during user creation", %{actor: actor} do
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs, actor: actor)
|
||||
|
||||
user_attrs = Map.put(@valid_user_attrs, :member, %{id: member.id})
|
||||
{:ok, user} = Accounts.create_user(user_attrs)
|
||||
{:ok, user} = Accounts.create_user(user_attrs, actor: actor)
|
||||
|
||||
# Load the relationship to test it
|
||||
{:ok, user_with_member} = Ash.get(Mv.Accounts.User, user.id, load: [:member])
|
||||
{:ok, user_with_member} =
|
||||
Ash.get(Mv.Accounts.User, user.id, actor: actor, load: [:member], actor: actor)
|
||||
|
||||
assert user_with_member.member.id == member.id
|
||||
end
|
||||
|
||||
test "member can be linked to user during member creation using manage_relationship" do
|
||||
{:ok, user} = Accounts.create_user(@valid_user_attrs)
|
||||
test "member can be linked to user during member creation using manage_relationship", %{
|
||||
actor: actor
|
||||
} do
|
||||
{:ok, user} = Accounts.create_user(@valid_user_attrs, actor: actor)
|
||||
|
||||
member_attrs = Map.put(@valid_member_attrs, :user, %{id: user.id})
|
||||
{:ok, member} = Membership.create_member(member_attrs)
|
||||
{:ok, member} = Membership.create_member(member_attrs, actor: actor)
|
||||
|
||||
# Load the relationship to test it
|
||||
{:ok, member_with_user} = Ash.get(Mv.Membership.Member, member.id, load: [:user])
|
||||
{:ok, member_with_user} =
|
||||
Ash.get(Mv.Membership.Member, member.id, actor: actor, load: [:user], actor: actor)
|
||||
|
||||
assert member_with_user.user.id == user.id
|
||||
end
|
||||
|
||||
test "user can be linked to member during update" do
|
||||
{:ok, user} = Accounts.create_user(@valid_user_attrs)
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
test "user can be linked to member during update", %{actor: actor} do
|
||||
{:ok, user} = Accounts.create_user(@valid_user_attrs, actor: actor)
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs, actor: actor)
|
||||
|
||||
{:ok, updated_user} = Accounts.update_user(user, %{member: %{id: member.id}})
|
||||
{:ok, updated_user} = Accounts.update_user(user, %{member: %{id: member.id}}, actor: actor)
|
||||
|
||||
# Load the relationship to test it
|
||||
{:ok, user_with_member} = Ash.get(Mv.Accounts.User, updated_user.id, load: [:member])
|
||||
{:ok, user_with_member} =
|
||||
Ash.get(Mv.Accounts.User, updated_user.id, actor: actor, load: [:member], actor: actor)
|
||||
|
||||
assert user_with_member.member.id == member.id
|
||||
end
|
||||
|
||||
test "member can be linked to user during update using manage_relationship" do
|
||||
{:ok, user} = Accounts.create_user(@valid_user_attrs)
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
test "member can be linked to user during update using manage_relationship", %{actor: actor} do
|
||||
{:ok, user} = Accounts.create_user(@valid_user_attrs, actor: actor)
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs, actor: actor)
|
||||
|
||||
{:ok, _updated_member} = Membership.update_member(member, %{user: %{id: user.id}})
|
||||
{:ok, _updated_member} =
|
||||
Membership.update_member(member, %{user: %{id: user.id}}, actor: actor)
|
||||
|
||||
# Load the relationship to test it
|
||||
{:ok, member_with_user} = Ash.get(Mv.Membership.Member, member.id, load: [:user])
|
||||
{:ok, member_with_user} =
|
||||
Ash.get(Mv.Membership.Member, member.id, actor: actor, load: [:user], actor: actor)
|
||||
|
||||
assert member_with_user.user.id == user.id
|
||||
end
|
||||
end
|
||||
|
|
@ -103,25 +123,39 @@ defmodule Mv.Accounts.UserMemberRelationshipTest do
|
|||
email: "bob@example.com"
|
||||
}
|
||||
|
||||
test "ash resolves inverse relationship automatically" do
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
setup do
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
%{actor: system_actor}
|
||||
end
|
||||
|
||||
test "ash resolves inverse relationship automatically", %{actor: actor} do
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs, actor: actor)
|
||||
|
||||
user_attrs = Map.put(@valid_user_attrs, :member, %{id: member.id})
|
||||
{:ok, user} = Accounts.create_user(user_attrs)
|
||||
{:ok, user} = Accounts.create_user(user_attrs, actor: actor)
|
||||
|
||||
# Load relationships
|
||||
{:ok, user_with_member} = Ash.get(Mv.Accounts.User, user.id, load: [:member])
|
||||
{:ok, member_with_user} = Ash.get(Mv.Membership.Member, member.id, load: [:user])
|
||||
{:ok, user_with_member} =
|
||||
Ash.get(Mv.Accounts.User, user.id, actor: actor, load: [:member], actor: actor)
|
||||
|
||||
{:ok, member_with_user} =
|
||||
Ash.get(Mv.Membership.Member, member.id, actor: actor, load: [:user], actor: actor)
|
||||
|
||||
assert user_with_member.member.id == member.id
|
||||
assert member_with_user.user.id == user.id
|
||||
end
|
||||
|
||||
test "member can find associated user" do
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
test "member can find associated user", %{actor: actor} do
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs, actor: actor)
|
||||
|
||||
{:ok, user} =
|
||||
Accounts.create_user(%{email: "test3@example.com", member: %{id: member.id}},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, member_with_user} =
|
||||
Ash.get(Mv.Membership.Member, member.id, actor: actor, load: [:user], actor: actor)
|
||||
|
||||
{:ok, user} = Accounts.create_user(%{email: "test3@example.com", member: %{id: member.id}})
|
||||
{:ok, member_with_user} = Ash.get(Mv.Membership.Member, member.id, load: [:user])
|
||||
assert member_with_user.user.id == user.id
|
||||
end
|
||||
end
|
||||
|
|
@ -137,61 +171,77 @@ defmodule Mv.Accounts.UserMemberRelationshipTest do
|
|||
email: "charlie@example.com"
|
||||
}
|
||||
|
||||
test "prevents overwriting a member of already linked user on update" do
|
||||
{:ok, existing_member} = Membership.create_member(@valid_member_attrs)
|
||||
setup do
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
%{actor: system_actor}
|
||||
end
|
||||
|
||||
test "prevents overwriting a member of already linked user on update", %{actor: actor} do
|
||||
{:ok, existing_member} = Membership.create_member(@valid_member_attrs, actor: actor)
|
||||
|
||||
user_attrs = Map.put(@valid_user_attrs, :member, %{id: existing_member.id})
|
||||
{:ok, user} = Accounts.create_user(user_attrs)
|
||||
{:ok, user} = Accounts.create_user(user_attrs, actor: actor)
|
||||
|
||||
{:ok, member2} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Dave",
|
||||
last_name: "Wilson",
|
||||
email: "dave@example.com"
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "Dave",
|
||||
last_name: "Wilson",
|
||||
email: "dave@example.com"
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
assert {:error, %Ash.Error.Invalid{}} =
|
||||
Accounts.update_user(user, %{member: %{id: member2.id}})
|
||||
Accounts.update_user(user, %{member: %{id: member2.id}}, actor: actor)
|
||||
end
|
||||
|
||||
test "prevents linking user to already linked member on update" do
|
||||
{:ok, existing_user} = Accounts.create_user(@valid_user_attrs)
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
test "prevents linking user to already linked member on update", %{actor: actor} do
|
||||
{:ok, existing_user} = Accounts.create_user(@valid_user_attrs, actor: actor)
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs, actor: actor)
|
||||
|
||||
{:ok, _updated_user} = Accounts.update_user(existing_user, %{member: %{id: member.id}})
|
||||
{:ok, _updated_user} =
|
||||
Accounts.update_user(existing_user, %{member: %{id: member.id}}, actor: actor)
|
||||
|
||||
{:ok, user2} = Accounts.create_user(%{email: "test5@example.com"})
|
||||
{:ok, user2} = Accounts.create_user(%{email: "test5@example.com"}, actor: actor)
|
||||
|
||||
assert {:error, %Ash.Error.Invalid{}} =
|
||||
Accounts.update_user(user2, %{member: %{id: member.id}})
|
||||
Accounts.update_user(user2, %{member: %{id: member.id}}, actor: actor)
|
||||
end
|
||||
|
||||
test "prevents linking member to already linked user on creation" do
|
||||
{:ok, existing_member} = Membership.create_member(@valid_member_attrs)
|
||||
test "prevents linking member to already linked user on creation", %{actor: actor} do
|
||||
{:ok, existing_member} = Membership.create_member(@valid_member_attrs, actor: actor)
|
||||
|
||||
user_attrs = Map.put(@valid_user_attrs, :member, %{id: existing_member.id})
|
||||
{:ok, user} = Accounts.create_user(user_attrs)
|
||||
{:ok, user} = Accounts.create_user(user_attrs, actor: actor)
|
||||
|
||||
assert {:error, %Ash.Error.Invalid{}} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Dave",
|
||||
last_name: "Wilson",
|
||||
email: "dave@example.com",
|
||||
user: %{id: user.id}
|
||||
})
|
||||
Membership.create_member(
|
||||
%{
|
||||
first_name: "Dave",
|
||||
last_name: "Wilson",
|
||||
email: "dave@example.com",
|
||||
user: %{id: user.id}
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
end
|
||||
|
||||
test "prevents linking user to already linked member on creation" do
|
||||
{:ok, existing_user} = Accounts.create_user(@valid_user_attrs)
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs)
|
||||
test "prevents linking user to already linked member on creation", %{actor: actor} do
|
||||
{:ok, existing_user} = Accounts.create_user(@valid_user_attrs, actor: actor)
|
||||
{:ok, member} = Membership.create_member(@valid_member_attrs, actor: actor)
|
||||
|
||||
{:ok, _updated_user} = Accounts.update_user(existing_user, %{member: %{id: member.id}})
|
||||
{:ok, _updated_user} =
|
||||
Accounts.update_user(existing_user, %{member: %{id: member.id}}, actor: actor)
|
||||
|
||||
assert {:error, %Ash.Error.Invalid{}} =
|
||||
Accounts.create_user(%{
|
||||
email: "test5@example.com",
|
||||
member: %{id: member.id}
|
||||
})
|
||||
Accounts.create_user(
|
||||
%{
|
||||
email: "test5@example.com",
|
||||
member: %{id: member.id}
|
||||
},
|
||||
actor: actor
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue