WIP feat: member user relation
This commit is contained in:
parent
ba79261d1d
commit
0dddeeb7a6
35 changed files with 1208 additions and 192 deletions
107
test/accounts/registration_member_test.exs
Normal file
107
test/accounts/registration_member_test.exs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
defmodule Mv.Accounts.RegistrationMemberTest do
|
||||
use Mv.DataCase
|
||||
|
||||
alias Mv.Accounts
|
||||
alias Mv.Accounts.User.MemberCreationNotifier
|
||||
|
||||
describe "registration creates member" do
|
||||
test "registering a user creates and assigns a new member via notifier" do
|
||||
# Create user first
|
||||
assert {:ok, user} =
|
||||
Accounts.register_with_password(%{
|
||||
email: "test@example.com",
|
||||
password: "password123"
|
||||
})
|
||||
|
||||
assert to_string(user.email) == "test@example.com"
|
||||
|
||||
# Manually trigger the notifier to test synchronously
|
||||
notification = %Ash.Notifier.Notification{
|
||||
action: %{name: :register_with_password},
|
||||
resource: Mv.Accounts.User,
|
||||
data: user
|
||||
}
|
||||
|
||||
assert :ok = MemberCreationNotifier.notify(notification)
|
||||
|
||||
# Reload user to get updated member_id
|
||||
user = Ash.reload!(user, domain: Mv.Accounts)
|
||||
|
||||
# User should have a member assigned
|
||||
assert user.member_id != nil
|
||||
|
||||
# Member should exist and have correct data
|
||||
member = Ash.get!(Mv.Membership.Member, user.member_id, domain: Mv.Membership)
|
||||
assert member.email == "test@example.com"
|
||||
assert member.first_name == "User"
|
||||
assert member.last_name == "Generated"
|
||||
end
|
||||
|
||||
test "notifier doesn't create member if user already has one" do
|
||||
# Create a member first
|
||||
{:ok, existing_member} =
|
||||
Mv.Membership.create_member(%{
|
||||
email: "existing@example.com",
|
||||
first_name: "Existing",
|
||||
last_name: "Member"
|
||||
})
|
||||
|
||||
# Create user with existing member
|
||||
assert {:ok, user} =
|
||||
Accounts.create_user(%{
|
||||
email: "existing@example.com",
|
||||
member_id: existing_member.id
|
||||
})
|
||||
|
||||
# Trigger notifier
|
||||
notification = %Ash.Notifier.Notification{
|
||||
action: %{name: :create_user},
|
||||
resource: Mv.Accounts.User,
|
||||
data: user
|
||||
}
|
||||
|
||||
assert :ok = MemberCreationNotifier.notify(notification)
|
||||
|
||||
# User should still have the same member
|
||||
user = Ash.reload!(user, domain: Mv.Accounts)
|
||||
assert user.member_id == existing_member.id
|
||||
|
||||
# Should not have created a new member
|
||||
member = Ash.get!(Mv.Membership.Member, user.member_id, domain: Mv.Membership)
|
||||
assert member.first_name == "Existing"
|
||||
assert member.last_name == "Member"
|
||||
end
|
||||
|
||||
test "member data has correct default values" do
|
||||
# Create user
|
||||
assert {:ok, user} =
|
||||
Accounts.create_user(%{
|
||||
email: "member@example.com"
|
||||
})
|
||||
|
||||
# Trigger notifier manually
|
||||
notification = %Ash.Notifier.Notification{
|
||||
action: %{name: :create_user},
|
||||
resource: Mv.Accounts.User,
|
||||
data: user
|
||||
}
|
||||
|
||||
assert :ok = MemberCreationNotifier.notify(notification)
|
||||
|
||||
# Reload and check member
|
||||
user = Ash.reload!(user, domain: Mv.Accounts)
|
||||
member = Ash.get!(Mv.Membership.Member, user.member_id, domain: Mv.Membership)
|
||||
|
||||
# Email should match user
|
||||
assert member.email == "member@example.com"
|
||||
|
||||
# Other fields should have default values
|
||||
assert member.first_name == "User"
|
||||
assert member.last_name == "Generated"
|
||||
# Allow both false and nil
|
||||
assert member.paid == false || member.paid == nil
|
||||
assert member.phone_number == nil
|
||||
assert member.birth_date == nil
|
||||
end
|
||||
end
|
||||
end
|
||||
20
test/accounts/user_delete_member_test.exs
Normal file
20
test/accounts/user_delete_member_test.exs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
defmodule Mv.Accounts.UserDeleteMemberTest do
|
||||
use Mv.DataCase, async: true
|
||||
alias Mv.Accounts
|
||||
alias Mv.Membership
|
||||
|
||||
describe "user deletion does not delete member" do
|
||||
test "deleting a user keeps the member in the system" do
|
||||
{:ok, member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Keep",
|
||||
last_name: "Me",
|
||||
email: "keepme@example.com"
|
||||
})
|
||||
|
||||
{:ok, user} = Accounts.create_user(%{email: "keepuser@example.com", member_id: member.id})
|
||||
:ok = Accounts.destroy_user(user)
|
||||
assert Membership.get_member!(member.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
77
test/accounts/user_member_integration_test.exs
Normal file
77
test/accounts/user_member_integration_test.exs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
defmodule Mv.Accounts.UserMemberIntegrationTest do
|
||||
use Mv.DataCase, async: true
|
||||
alias Mv.Accounts
|
||||
alias Mv.Membership
|
||||
alias Mv.Accounts.User.MemberCreationNotifier
|
||||
|
||||
describe "User-Member-Relation" do
|
||||
test "ein User kann einem Member zugeordnet werden" do
|
||||
{:ok, member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Max",
|
||||
last_name: "Mustermann",
|
||||
email: "max@example.com"
|
||||
})
|
||||
|
||||
{:ok, user} = Accounts.create_user(%{email: "user1@example.com", member_id: member.id})
|
||||
assert user.member_id == member.id
|
||||
end
|
||||
|
||||
test "ein Member kann nur einem User zugeordnet werden (unique constraint)" do
|
||||
{:ok, member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Anna",
|
||||
last_name: "Test",
|
||||
email: "anna@example.com"
|
||||
})
|
||||
|
||||
{:ok, user1} = Accounts.create_user(%{email: "user2@example.com", member_id: member.id})
|
||||
assert user1.member_id == member.id
|
||||
|
||||
{:error, %Ash.Error.Invalid{errors: errors}} =
|
||||
Accounts.create_user(%{email: "user3@example.com", member_id: member.id})
|
||||
|
||||
assert Enum.any?(errors, fn error ->
|
||||
error.message =~ "already been taken" or error.field == :member_id
|
||||
end)
|
||||
end
|
||||
|
||||
test "ein User ohne Member ist nicht erlaubt (bei Registrierung/Erstellung)" do
|
||||
# Create user without member first
|
||||
result = Accounts.create_user(%{email: "user4@example.com"})
|
||||
|
||||
case result do
|
||||
{:ok, user} ->
|
||||
# User is created but doesn't have member yet
|
||||
assert user.member_id == nil
|
||||
|
||||
# Manually trigger the notifier to simulate automatic member creation
|
||||
notification = %Ash.Notifier.Notification{
|
||||
action: %{name: :create_user},
|
||||
resource: Mv.Accounts.User,
|
||||
data: user
|
||||
}
|
||||
|
||||
assert :ok = MemberCreationNotifier.notify(notification)
|
||||
|
||||
# Reload user and verify member was created and assigned
|
||||
user = Ash.reload!(user, domain: Mv.Accounts)
|
||||
assert user.member_id, "User should have a member_id assigned after notifier"
|
||||
|
||||
{:error, _} ->
|
||||
flunk("User creation should succeed")
|
||||
end
|
||||
end
|
||||
|
||||
test "ein Member kann ohne User existieren" do
|
||||
{:ok, member} =
|
||||
Membership.create_member(%{
|
||||
first_name: "Lisa",
|
||||
last_name: "Solo",
|
||||
email: "lisa@example.com"
|
||||
})
|
||||
|
||||
assert member.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -119,8 +119,10 @@ defmodule MvWeb.OidcIntegrationTest do
|
|||
|
||||
assert {:error, %Ash.Error.Invalid{errors: errors}} = result
|
||||
|
||||
# Check for either Required error or InvalidAttribute error with email field
|
||||
assert Enum.any?(errors, fn err ->
|
||||
match?(%Ash.Error.Changes.Required{field: :email}, err)
|
||||
match?(%Ash.Error.Changes.Required{field: :email}, err) or
|
||||
match?(%Ash.Error.Changes.InvalidAttribute{field: :email}, err)
|
||||
end)
|
||||
end
|
||||
|
||||
|
|
|
|||
81
test/mv_web/user_live/admin_member_assignment_test.exs
Normal file
81
test/mv_web/user_live/admin_member_assignment_test.exs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
defmodule MvWeb.UserLive.AdminMemberAssignmentTest do
|
||||
use MvWeb.ConnCase, async: true
|
||||
import Phoenix.LiveViewTest
|
||||
|
||||
test "admin can assign existing member without user to new user", %{conn: conn} do
|
||||
{:ok, member} =
|
||||
Mv.Membership.create_member(%{
|
||||
first_name: "Admin",
|
||||
last_name: "Choice",
|
||||
email: "adminchoice@example.com"
|
||||
})
|
||||
|
||||
conn = conn_with_oidc_user(conn, %{email: "admin@example.com"})
|
||||
{:ok, view, _html} = live(conn, "/users/new")
|
||||
|
||||
# First click the "assign existing member" radio button
|
||||
view
|
||||
|> element("input[value='assign_existing']")
|
||||
|> render_click()
|
||||
|
||||
# Then submit the form with member_id
|
||||
view
|
||||
|> form("#user-form", user: %{email: "adminuser@example.com", member_id: member.id})
|
||||
|> render_submit()
|
||||
|
||||
user =
|
||||
Ash.get!(Mv.Accounts.User, [email: Ash.CiString.new("adminuser@example.com")],
|
||||
domain: Mv.Accounts
|
||||
)
|
||||
|
||||
assert user.member_id == member.id
|
||||
end
|
||||
|
||||
test "admin can create new member for user if none selected", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn, %{email: "admin2@example.com"})
|
||||
{:ok, view, _html} = live(conn, "/users/new")
|
||||
|
||||
# Default mode is "create_new", so just submit
|
||||
view
|
||||
|> form("#user-form", user: %{email: "adminnewmember@example.com"})
|
||||
|> render_submit()
|
||||
|
||||
user =
|
||||
Ash.get!(Mv.Accounts.User, [email: Ash.CiString.new("adminnewmember@example.com")],
|
||||
domain: Mv.Accounts
|
||||
)
|
||||
|
||||
assert user.member_id
|
||||
{:ok, member} = Mv.Membership.get_member!(user.member_id)
|
||||
assert member.email == "adminnewmember@example.com"
|
||||
end
|
||||
|
||||
test "admin cannot assign member that already has a user", %{conn: conn} do
|
||||
{:ok, member} =
|
||||
Mv.Membership.create_member(%{
|
||||
first_name: "Taken",
|
||||
last_name: "Member",
|
||||
email: "taken@example.com"
|
||||
})
|
||||
|
||||
{:ok, _user} =
|
||||
Mv.Accounts.create_user(%{email: "takenuser@example.com", member_id: member.id})
|
||||
|
||||
conn = conn_with_oidc_user(conn, %{email: "admin3@example.com"})
|
||||
{:ok, view, _html} = live(conn, "/users/new")
|
||||
|
||||
# First click the "assign existing member" radio button
|
||||
view
|
||||
|> element("input[value='assign_existing']")
|
||||
|> render_click()
|
||||
|
||||
# Check that the already assigned member is NOT in the dropdown options
|
||||
html = render(view)
|
||||
# Member should not be available for selection
|
||||
refute html =~ "Taken Member"
|
||||
|
||||
# The dropdown should have limited or no options since member is already taken
|
||||
# Placeholder should be visible
|
||||
assert html =~ "Choose a member..."
|
||||
end
|
||||
end
|
||||
|
|
@ -244,7 +244,9 @@ defmodule MvWeb.UserLive.FormTest do
|
|||
flunk("Expected validation error but form was submitted successfully")
|
||||
|
||||
html when is_binary(html) ->
|
||||
assert html =~ "must have length of at least 8"
|
||||
# Allow for different validation message sources
|
||||
assert html =~ "length must be greater than or equal to 8" or
|
||||
html =~ "must have length of at least 8"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
36
test/mv_web/user_live/member_display_test.exs
Normal file
36
test/mv_web/user_live/member_display_test.exs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
defmodule MvWeb.UserLive.MemberDisplayTest do
|
||||
use MvWeb.ConnCase, async: true
|
||||
import Phoenix.LiveViewTest
|
||||
|
||||
test "User-Liste zeigt zugeordneten Member an", %{conn: conn} do
|
||||
{:ok, member} =
|
||||
Mv.Membership.create_member(%{
|
||||
first_name: "Max",
|
||||
last_name: "Mustermann",
|
||||
email: "max@example.com"
|
||||
})
|
||||
|
||||
{:ok, _user} = Mv.Accounts.create_user(%{email: "user5@example.com", member_id: member.id})
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, _view, html} = live(conn, "/users")
|
||||
assert html =~ "Max Mustermann"
|
||||
# User email, not member email
|
||||
assert html =~ "user5@example.com"
|
||||
end
|
||||
|
||||
test "User-Detailansicht zeigt Member-Daten an", %{conn: conn} do
|
||||
{:ok, member} =
|
||||
Mv.Membership.create_member(%{
|
||||
first_name: "Anna",
|
||||
last_name: "Test",
|
||||
email: "anna@example.com"
|
||||
})
|
||||
|
||||
{:ok, user} = Mv.Accounts.create_user(%{email: "user6@example.com", member_id: member.id})
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, _view, html} = live(conn, "/users/#{user.id}")
|
||||
assert html =~ "Anna Test"
|
||||
# In detail view, member email should be shown
|
||||
assert html =~ "anna@example.com"
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue