Tests: use code interface for Member create/update (actor propagation)
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone/promote/production Build is passing

This commit is contained in:
Moritz 2026-01-29 15:30:14 +01:00 committed by moritz
parent 5a2f035ecc
commit 4473cfd372
32 changed files with 733 additions and 818 deletions

View file

@ -239,13 +239,14 @@ defmodule Mv.Membership.CustomFieldDeletionTest do
# Helper functions # Helper functions
defp create_member(actor) do defp create_member(actor) do
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "User#{System.unique_integer([:positive])}", last_name: "User#{System.unique_integer([:positive])}",
email: "test#{System.unique_integer([:positive])}@example.com" email: "test#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create(actor: actor) actor: actor
)
end end
defp create_custom_field(name, value_type, actor) do defp create_custom_field(name, value_type, actor) do

View file

@ -17,13 +17,14 @@ defmodule Mv.Membership.CustomFieldValueValidationTest do
# Create a test member # Create a test member
{:ok, member} = {:ok, member} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "User", last_name: "User",
email: "test.validation@example.com" email: "test.validation@example.com"
}) },
|> Ash.create(actor: system_actor) actor: system_actor
)
# Create custom fields for different types # Create custom fields for different types
{:ok, string_field} = {:ok, string_field} =

View file

@ -38,10 +38,8 @@ defmodule Mv.Membership.MemberCycleCalculationsTest do
} }
attrs = Map.merge(default_attrs, attrs) attrs = Map.merge(default_attrs, attrs)
{:ok, member} = Mv.Membership.create_member(attrs, actor: actor)
Member member
|> Ash.Changeset.for_create(:create_member, attrs)
|> Ash.create!(actor: actor)
end end
# Helper to create a cycle # Helper to create a cycle

View file

@ -14,31 +14,22 @@ defmodule Mv.Membership.MemberSearchWithCustomFieldsTest do
# Create test members # Create test members
{:ok, member1} = {:ok, member1} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "Alice", last_name: "Anderson", email: "alice@example.com"},
first_name: "Alice", actor: system_actor
last_name: "Anderson", )
email: "alice@example.com"
})
|> Ash.create(actor: system_actor)
{:ok, member2} = {:ok, member2} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "Bob", last_name: "Brown", email: "bob@example.com"},
first_name: "Bob", actor: system_actor
last_name: "Brown", )
email: "bob@example.com"
})
|> Ash.create(actor: system_actor)
{:ok, member3} = {:ok, member3} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "Charlie", last_name: "Clark", email: "charlie@example.com"},
first_name: "Charlie", actor: system_actor
last_name: "Clark", )
email: "charlie@example.com"
})
|> Ash.create(actor: system_actor)
# Create custom fields for different types # Create custom fields for different types
{:ok, string_field} = {:ok, string_field} =
@ -112,9 +103,7 @@ defmodule Mv.Membership.MemberSearchWithCustomFieldsTest do
# Force search_vector update by reloading member # Force search_vector update by reloading member
{:ok, _updated_member} = {:ok, _updated_member} =
member1 Mv.Membership.update_member(member1, %{}, actor: system_actor)
|> Ash.Changeset.for_update(:update_member, %{})
|> Ash.update(actor: system_actor)
# Search for the custom field value # Search for the custom field value
results = results =
@ -143,9 +132,7 @@ defmodule Mv.Membership.MemberSearchWithCustomFieldsTest do
# Force search_vector update # Force search_vector update
{:ok, _updated_member} = {:ok, _updated_member} =
member1 Mv.Membership.update_member(member1, %{}, actor: system_actor)
|> Ash.Changeset.for_update(:update_member, %{})
|> Ash.update(actor: system_actor)
# Search for the custom field value # Search for the custom field value
results = results =
@ -174,9 +161,7 @@ defmodule Mv.Membership.MemberSearchWithCustomFieldsTest do
# Force search_vector update # Force search_vector update
{:ok, _updated_member} = {:ok, _updated_member} =
member1 Mv.Membership.update_member(member1, %{}, actor: system_actor)
|> Ash.Changeset.for_update(:update_member, %{})
|> Ash.update(actor: system_actor)
# Search for partial custom field value (should work via FTS or custom field filter) # Search for partial custom field value (should work via FTS or custom field filter)
results = results =
@ -225,9 +210,7 @@ defmodule Mv.Membership.MemberSearchWithCustomFieldsTest do
# Force search_vector update # Force search_vector update
{:ok, _updated_member} = {:ok, _updated_member} =
member1 Mv.Membership.update_member(member1, %{}, actor: system_actor)
|> Ash.Changeset.for_update(:update_member, %{})
|> Ash.update(actor: system_actor)
# Search for the custom field value (date is stored as text in search_vector) # Search for the custom field value (date is stored as text in search_vector)
results = results =
@ -256,9 +239,7 @@ defmodule Mv.Membership.MemberSearchWithCustomFieldsTest do
# Force search_vector update # Force search_vector update
{:ok, _updated_member} = {:ok, _updated_member} =
member1 Mv.Membership.update_member(member1, %{}, actor: system_actor)
|> Ash.Changeset.for_update(:update_member, %{})
|> Ash.update(actor: system_actor)
# Search for the custom field value (boolean is stored as "true" or "false" text) # Search for the custom field value (boolean is stored as "true" or "false" text)
results = results =
@ -287,9 +268,7 @@ defmodule Mv.Membership.MemberSearchWithCustomFieldsTest do
# Force search_vector update # Force search_vector update
{:ok, _updated_member} = {:ok, _updated_member} =
member1 Mv.Membership.update_member(member1, %{}, actor: system_actor)
|> Ash.Changeset.for_update(:update_member, %{})
|> Ash.update(actor: system_actor)
# Update custom field value # Update custom field value
{:ok, _updated_cfv} = {:ok, _updated_cfv} =
@ -334,9 +313,7 @@ defmodule Mv.Membership.MemberSearchWithCustomFieldsTest do
# Force search_vector update # Force search_vector update
{:ok, _updated_member} = {:ok, _updated_member} =
member1 Mv.Membership.update_member(member1, %{}, actor: system_actor)
|> Ash.Changeset.for_update(:update_member, %{})
|> Ash.update(actor: system_actor)
# Verify it's searchable # Verify it's searchable
results = results =
@ -401,9 +378,7 @@ defmodule Mv.Membership.MemberSearchWithCustomFieldsTest do
# Update member (should trigger search_vector update including custom fields) # Update member (should trigger search_vector update including custom fields)
{:ok, _updated_member} = {:ok, _updated_member} =
member1 Mv.Membership.update_member(member1, %{notes: "Updated notes"}, actor: system_actor)
|> Ash.Changeset.for_update(:update_member, %{notes: "Updated notes"})
|> Ash.update(actor: system_actor)
# Search should find the custom field value # Search should find the custom field value
results = results =
@ -452,9 +427,7 @@ defmodule Mv.Membership.MemberSearchWithCustomFieldsTest do
# Force search_vector update # Force search_vector update
{:ok, _updated_member} = {:ok, _updated_member} =
member1 Mv.Membership.update_member(member1, %{}, actor: system_actor)
|> Ash.Changeset.for_update(:update_member, %{})
|> Ash.update(actor: system_actor)
# All values should be searchable # All values should be searchable
results1 = results1 =
@ -496,9 +469,7 @@ defmodule Mv.Membership.MemberSearchWithCustomFieldsTest do
# Force search_vector update # Force search_vector update
{:ok, _updated_member} = {:ok, _updated_member} =
member1 Mv.Membership.update_member(member1, %{}, actor: system_actor)
|> Ash.Changeset.for_update(:update_member, %{})
|> Ash.update(actor: system_actor)
# Search for full value (should work via search_vector) # Search for full value (should work via search_vector)
results_full = results_full =
@ -541,9 +512,7 @@ defmodule Mv.Membership.MemberSearchWithCustomFieldsTest do
# Force search_vector update # Force search_vector update
{:ok, _updated_member} = {:ok, _updated_member} =
member1 Mv.Membership.update_member(member1, %{}, actor: system_actor)
|> Ash.Changeset.for_update(:update_member, %{})
|> Ash.update(actor: system_actor)
# Search for full phone number (should work via search_vector) # Search for full phone number (should work via search_vector)
results_full = results_full =

View file

@ -41,10 +41,8 @@ defmodule Mv.Membership.MemberTypeChangeIntegrationTest do
} }
attrs = Map.merge(default_attrs, attrs) attrs = Map.merge(default_attrs, attrs)
{:ok, member} = Mv.Membership.create_member(attrs, actor: actor)
Member member
|> Ash.Changeset.for_create(:create_member, attrs)
|> Ash.create!(actor: actor)
end end
# Helper to create a cycle # Helper to create a cycle
@ -76,10 +74,14 @@ defmodule Mv.Membership.MemberTypeChangeIntegrationTest do
# Manually assign fee type (this will trigger cycle generation) # Manually assign fee type (this will trigger cycle generation)
member = member =
member member
|> Ash.Changeset.for_update(:update_member, %{ |> then(fn m ->
membership_fee_type_id: yearly_type1.id {:ok, updated} =
}) Mv.Membership.update_member(m, %{membership_fee_type_id: yearly_type1.id},
|> Ash.update!(actor: actor) actor: actor
)
updated
end)
# Cycle generation runs synchronously in the same transaction # Cycle generation runs synchronously in the same transaction
# No need to wait for async completion # No need to wait for async completion
@ -140,11 +142,9 @@ defmodule Mv.Membership.MemberTypeChangeIntegrationTest do
# Change membership fee type (same interval, different amount) # Change membership fee type (same interval, different amount)
assert {:ok, _updated_member} = assert {:ok, _updated_member} =
member Mv.Membership.update_member(member, %{membership_fee_type_id: yearly_type2.id},
|> Ash.Changeset.for_update(:update_member, %{ actor: actor
membership_fee_type_id: yearly_type2.id )
})
|> Ash.update(actor: actor)
# Cycle regeneration runs synchronously in the same transaction # Cycle regeneration runs synchronously in the same transaction
# No need to wait for async completion # No need to wait for async completion
@ -194,10 +194,14 @@ defmodule Mv.Membership.MemberTypeChangeIntegrationTest do
# Manually assign fee type (this will trigger cycle generation) # Manually assign fee type (this will trigger cycle generation)
member = member =
member member
|> Ash.Changeset.for_update(:update_member, %{ |> then(fn m ->
membership_fee_type_id: yearly_type1.id {:ok, updated} =
}) Mv.Membership.update_member(m, %{membership_fee_type_id: yearly_type1.id},
|> Ash.update!(actor: actor) actor: actor
)
updated
end)
# Cycle generation runs synchronously in the same transaction # Cycle generation runs synchronously in the same transaction
# No need to wait for async completion # No need to wait for async completion
@ -215,11 +219,9 @@ defmodule Mv.Membership.MemberTypeChangeIntegrationTest do
# Change membership fee type # Change membership fee type
assert {:ok, _updated_member} = assert {:ok, _updated_member} =
member Mv.Membership.update_member(member, %{membership_fee_type_id: yearly_type2.id},
|> Ash.Changeset.for_update(:update_member, %{ actor: actor
membership_fee_type_id: yearly_type2.id )
})
|> Ash.update(actor: actor)
# Cycle regeneration runs synchronously in the same transaction # Cycle regeneration runs synchronously in the same transaction
# No need to wait for async completion # No need to wait for async completion
@ -242,10 +244,14 @@ defmodule Mv.Membership.MemberTypeChangeIntegrationTest do
# Manually assign fee type (this will trigger cycle generation) # Manually assign fee type (this will trigger cycle generation)
member = member =
member member
|> Ash.Changeset.for_update(:update_member, %{ |> then(fn m ->
membership_fee_type_id: yearly_type1.id {:ok, updated} =
}) Mv.Membership.update_member(m, %{membership_fee_type_id: yearly_type1.id},
|> Ash.update!(actor: actor) actor: actor
)
updated
end)
# Cycle generation runs synchronously in the same transaction # Cycle generation runs synchronously in the same transaction
# No need to wait for async completion # No need to wait for async completion
@ -263,11 +269,9 @@ defmodule Mv.Membership.MemberTypeChangeIntegrationTest do
# Change membership fee type # Change membership fee type
assert {:ok, _updated_member} = assert {:ok, _updated_member} =
member Mv.Membership.update_member(member, %{membership_fee_type_id: yearly_type2.id},
|> Ash.Changeset.for_update(:update_member, %{ actor: actor
membership_fee_type_id: yearly_type2.id )
})
|> Ash.update(actor: actor)
# Cycle regeneration runs synchronously in the same transaction # Cycle regeneration runs synchronously in the same transaction
# No need to wait for async completion # No need to wait for async completion
@ -290,10 +294,14 @@ defmodule Mv.Membership.MemberTypeChangeIntegrationTest do
# Manually assign fee type (this will trigger cycle generation) # Manually assign fee type (this will trigger cycle generation)
member = member =
member member
|> Ash.Changeset.for_update(:update_member, %{ |> then(fn m ->
membership_fee_type_id: yearly_type1.id {:ok, updated} =
}) Mv.Membership.update_member(m, %{membership_fee_type_id: yearly_type1.id},
|> Ash.update!(actor: actor) actor: actor
)
updated
end)
# Cycle generation runs synchronously in the same transaction # Cycle generation runs synchronously in the same transaction
# No need to wait for async completion # No need to wait for async completion
@ -357,11 +365,9 @@ defmodule Mv.Membership.MemberTypeChangeIntegrationTest do
# Change membership fee type # Change membership fee type
assert {:ok, _updated_member} = assert {:ok, _updated_member} =
member Mv.Membership.update_member(member, %{membership_fee_type_id: yearly_type2.id},
|> Ash.Changeset.for_update(:update_member, %{ actor: actor
membership_fee_type_id: yearly_type2.id )
})
|> Ash.update(actor: actor)
# Cycle regeneration runs synchronously in the same transaction # Cycle regeneration runs synchronously in the same transaction
# No need to wait for async completion # No need to wait for async completion
@ -406,10 +412,14 @@ defmodule Mv.Membership.MemberTypeChangeIntegrationTest do
# Manually assign fee type (this will trigger cycle generation) # Manually assign fee type (this will trigger cycle generation)
member = member =
member member
|> Ash.Changeset.for_update(:update_member, %{ |> then(fn m ->
membership_fee_type_id: yearly_type1.id {:ok, updated} =
}) Mv.Membership.update_member(m, %{membership_fee_type_id: yearly_type1.id},
|> Ash.update!(actor: actor) actor: actor
)
updated
end)
# Cycle generation runs synchronously in the same transaction # Cycle generation runs synchronously in the same transaction
# No need to wait for async completion # No need to wait for async completion
@ -463,11 +473,9 @@ defmodule Mv.Membership.MemberTypeChangeIntegrationTest do
# Change membership fee type # Change membership fee type
assert {:ok, updated_member} = assert {:ok, updated_member} =
member Mv.Membership.update_member(member, %{membership_fee_type_id: yearly_type2.id},
|> Ash.Changeset.for_update(:update_member, %{ actor: actor
membership_fee_type_id: yearly_type2.id )
})
|> Ash.update(actor: actor)
# Cycle regeneration runs synchronously in the same transaction # Cycle regeneration runs synchronously in the same transaction
# No need to wait for async completion # No need to wait for async completion

View file

@ -146,16 +146,17 @@ defmodule Mv.MembershipFees.Changes.SetMembershipFeeStartDateTest do
|> Ash.create!(actor: actor) |> Ash.create!(actor: actor)
# Create member with join_date and fee type but no explicit start date # Create member with join_date and fee type but no explicit start date
member = {:ok, member} =
Mv.Membership.Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "User", last_name: "User",
email: "test#{System.unique_integer([:positive])}@example.com", email: "test#{System.unique_integer([:positive])}@example.com",
join_date: ~D[2024-03-15], join_date: ~D[2024-03-15],
membership_fee_type_id: fee_type.id membership_fee_type_id: fee_type.id
}) },
|> Ash.create!(actor: actor) actor: actor
)
# Should have auto-calculated start date (2024-01-01 for yearly with include_joining_cycle=true) # Should have auto-calculated start date (2024-01-01 for yearly with include_joining_cycle=true)
assert member.membership_fee_start_date == ~D[2024-01-01] assert member.membership_fee_start_date == ~D[2024-01-01]
@ -177,17 +178,18 @@ defmodule Mv.MembershipFees.Changes.SetMembershipFeeStartDateTest do
# Create member with explicit start date # Create member with explicit start date
manual_start_date = ~D[2024-07-01] manual_start_date = ~D[2024-07-01]
member = {:ok, member} =
Mv.Membership.Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "User", last_name: "User",
email: "test#{System.unique_integer([:positive])}@example.com", email: "test#{System.unique_integer([:positive])}@example.com",
join_date: ~D[2024-03-15], join_date: ~D[2024-03-15],
membership_fee_type_id: fee_type.id, membership_fee_type_id: fee_type.id,
membership_fee_start_date: manual_start_date membership_fee_start_date: manual_start_date
}) },
|> Ash.create!(actor: actor) actor: actor
)
# Should keep the manually set date # Should keep the manually set date
assert member.membership_fee_start_date == manual_start_date assert member.membership_fee_start_date == manual_start_date
@ -207,16 +209,17 @@ defmodule Mv.MembershipFees.Changes.SetMembershipFeeStartDateTest do
|> Ash.create!(actor: actor) |> Ash.create!(actor: actor)
# Create member # Create member
member = {:ok, member} =
Mv.Membership.Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "User", last_name: "User",
email: "test#{System.unique_integer([:positive])}@example.com", email: "test#{System.unique_integer([:positive])}@example.com",
join_date: ~D[2024-03-15], join_date: ~D[2024-03-15],
membership_fee_type_id: fee_type.id membership_fee_type_id: fee_type.id
}) },
|> Ash.create!(actor: actor) actor: actor
)
# Should have next cycle start date (2025-01-01 for yearly with include_joining_cycle=false) # Should have next cycle start date (2025-01-01 for yearly with include_joining_cycle=false)
assert member.membership_fee_start_date == ~D[2025-01-01] assert member.membership_fee_start_date == ~D[2025-01-01]
@ -236,16 +239,17 @@ defmodule Mv.MembershipFees.Changes.SetMembershipFeeStartDateTest do
|> Ash.create!(actor: actor) |> Ash.create!(actor: actor)
# Create member without join_date # Create member without join_date
member = {:ok, member} =
Mv.Membership.Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "User", last_name: "User",
email: "test#{System.unique_integer([:positive])}@example.com", email: "test#{System.unique_integer([:positive])}@example.com",
membership_fee_type_id: fee_type.id membership_fee_type_id: fee_type.id
# No join_date # No join_date
}) },
|> Ash.create!(actor: actor) actor: actor
)
# Should not have auto-calculated start date # Should not have auto-calculated start date
assert is_nil(member.membership_fee_start_date) assert is_nil(member.membership_fee_start_date)
@ -255,16 +259,17 @@ defmodule Mv.MembershipFees.Changes.SetMembershipFeeStartDateTest do
setup_settings(true, actor) setup_settings(true, actor)
# Create member without fee type # Create member without fee type
member = {:ok, member} =
Mv.Membership.Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "User", last_name: "User",
email: "test#{System.unique_integer([:positive])}@example.com", email: "test#{System.unique_integer([:positive])}@example.com",
join_date: ~D[2024-03-15] join_date: ~D[2024-03-15]
# No membership_fee_type_id # No membership_fee_type_id
}) },
|> Ash.create!(actor: actor) actor: actor
)
# Should not have auto-calculated start date # Should not have auto-calculated start date
assert is_nil(member.membership_fee_start_date) assert is_nil(member.membership_fee_start_date)

View file

@ -4,7 +4,6 @@ defmodule Mv.MembershipFees.Changes.ValidateSameIntervalTest do
""" """
use Mv.DataCase, async: true use Mv.DataCase, async: true
alias Mv.Membership.Member
alias Mv.MembershipFees.MembershipFeeType alias Mv.MembershipFees.MembershipFeeType
alias Mv.MembershipFees.Changes.ValidateSameInterval alias Mv.MembershipFees.Changes.ValidateSameInterval
@ -37,10 +36,8 @@ defmodule Mv.MembershipFees.Changes.ValidateSameIntervalTest do
} }
attrs = Map.merge(default_attrs, attrs) attrs = Map.merge(default_attrs, attrs)
{:ok, member} = Mv.Membership.create_member(attrs, actor: actor)
Member member
|> Ash.Changeset.for_create(:create_member, attrs)
|> Ash.create!(actor: actor)
end end
describe "validate_interval_match/1" do describe "validate_interval_match/1" do
@ -52,9 +49,9 @@ defmodule Mv.MembershipFees.Changes.ValidateSameIntervalTest do
changeset = changeset =
member member
|> Ash.Changeset.for_update(:update_member, %{ |> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: yearly_type2.id},
membership_fee_type_id: yearly_type2.id actor: actor
}) )
|> ValidateSameInterval.change(%{}, %{}) |> ValidateSameInterval.change(%{}, %{})
assert changeset.valid? assert changeset.valid?
@ -68,9 +65,9 @@ defmodule Mv.MembershipFees.Changes.ValidateSameIntervalTest do
changeset = changeset =
member member
|> Ash.Changeset.for_update(:update_member, %{ |> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: monthly_type.id},
membership_fee_type_id: monthly_type.id actor: actor
}) )
|> ValidateSameInterval.change(%{}, %{}) |> ValidateSameInterval.change(%{}, %{})
refute changeset.valid? refute changeset.valid?
@ -90,9 +87,9 @@ defmodule Mv.MembershipFees.Changes.ValidateSameIntervalTest do
changeset = changeset =
member member
|> Ash.Changeset.for_update(:update_member, %{ |> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: yearly_type.id},
membership_fee_type_id: yearly_type.id actor: actor
}) )
|> ValidateSameInterval.change(%{}, %{}) |> ValidateSameInterval.change(%{}, %{})
assert changeset.valid? assert changeset.valid?
@ -104,9 +101,7 @@ defmodule Mv.MembershipFees.Changes.ValidateSameIntervalTest do
changeset = changeset =
member member
|> Ash.Changeset.for_update(:update_member, %{ |> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: nil}, actor: actor)
membership_fee_type_id: nil
})
|> ValidateSameInterval.change(%{}, %{}) |> ValidateSameInterval.change(%{}, %{})
refute changeset.valid? refute changeset.valid?
@ -124,9 +119,7 @@ defmodule Mv.MembershipFees.Changes.ValidateSameIntervalTest do
changeset = changeset =
member member
|> Ash.Changeset.for_update(:update_member, %{ |> Ash.Changeset.for_update(:update_member, %{first_name: "New Name"}, actor: actor)
first_name: "New Name"
})
|> ValidateSameInterval.change(%{}, %{}) |> ValidateSameInterval.change(%{}, %{})
assert changeset.valid? assert changeset.valid?
@ -140,9 +133,9 @@ defmodule Mv.MembershipFees.Changes.ValidateSameIntervalTest do
changeset = changeset =
member member
|> Ash.Changeset.for_update(:update_member, %{ |> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: quarterly_type.id},
membership_fee_type_id: quarterly_type.id actor: actor
}) )
|> ValidateSameInterval.change(%{}, %{}) |> ValidateSameInterval.change(%{}, %{})
error = Enum.find(changeset.errors, &(&1.field == :membership_fee_type_id)) error = Enum.find(changeset.errors, &(&1.field == :membership_fee_type_id))
@ -179,9 +172,9 @@ defmodule Mv.MembershipFees.Changes.ValidateSameIntervalTest do
changeset = changeset =
member member
|> Ash.Changeset.for_update(:update_member, %{ |> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: type2.id},
membership_fee_type_id: type2.id actor: actor
}) )
|> ValidateSameInterval.change(%{}, %{}) |> ValidateSameInterval.change(%{}, %{})
refute changeset.valid?, refute changeset.valid?,
@ -199,11 +192,9 @@ defmodule Mv.MembershipFees.Changes.ValidateSameIntervalTest do
# Try to update member with different interval type # Try to update member with different interval type
assert {:error, %Ash.Error.Invalid{} = error} = assert {:error, %Ash.Error.Invalid{} = error} =
member Mv.Membership.update_member(member, %{membership_fee_type_id: monthly_type.id},
|> Ash.Changeset.for_update(:update_member, %{ actor: actor
membership_fee_type_id: monthly_type.id )
})
|> Ash.update(actor: actor)
# Check that error is about interval mismatch # Check that error is about interval mismatch
error_message = extract_error_message(error) error_message = extract_error_message(error)
@ -220,11 +211,9 @@ defmodule Mv.MembershipFees.Changes.ValidateSameIntervalTest do
# Update member with same-interval type # Update member with same-interval type
assert {:ok, updated_member} = assert {:ok, updated_member} =
member Mv.Membership.update_member(member, %{membership_fee_type_id: yearly_type2.id},
|> Ash.Changeset.for_update(:update_member, %{ actor: actor
membership_fee_type_id: yearly_type2.id )
})
|> Ash.update(actor: actor)
assert updated_member.membership_fee_type_id == yearly_type2.id assert updated_member.membership_fee_type_id == yearly_type2.id
end end

View file

@ -52,16 +52,17 @@ defmodule Mv.MembershipFees.MemberCycleIntegrationTest do
setup_settings(true, actor) setup_settings(true, actor)
fee_type = create_fee_type(%{interval: :yearly}, actor) fee_type = create_fee_type(%{interval: :yearly}, actor)
member = {:ok, member} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "User", last_name: "User",
email: "test#{System.unique_integer([:positive])}@example.com", email: "test#{System.unique_integer([:positive])}@example.com",
join_date: ~D[2023-03-15], join_date: ~D[2023-03-15],
membership_fee_type_id: fee_type.id membership_fee_type_id: fee_type.id
}) },
|> Ash.create!(actor: actor) actor: actor
)
cycles = get_member_cycles(member.id, actor) cycles = get_member_cycles(member.id, actor)
@ -80,16 +81,16 @@ defmodule Mv.MembershipFees.MemberCycleIntegrationTest do
test "does not create cycles when member has no fee type", %{actor: actor} do test "does not create cycles when member has no fee type", %{actor: actor} do
setup_settings(true, actor) setup_settings(true, actor)
member = {:ok, member} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "User", last_name: "User",
email: "test#{System.unique_integer([:positive])}@example.com", email: "test#{System.unique_integer([:positive])}@example.com",
join_date: ~D[2023-03-15] join_date: ~D[2023-03-15]
# No membership_fee_type_id },
}) actor: actor
|> Ash.create!(actor: actor) )
cycles = get_member_cycles(member.id, actor) cycles = get_member_cycles(member.id, actor)
@ -100,16 +101,16 @@ defmodule Mv.MembershipFees.MemberCycleIntegrationTest do
setup_settings(true, actor) setup_settings(true, actor)
fee_type = create_fee_type(%{interval: :yearly}, actor) fee_type = create_fee_type(%{interval: :yearly}, actor)
member = {:ok, member} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "User", last_name: "User",
email: "test#{System.unique_integer([:positive])}@example.com", email: "test#{System.unique_integer([:positive])}@example.com",
membership_fee_type_id: fee_type.id membership_fee_type_id: fee_type.id
# No join_date },
}) actor: actor
|> Ash.create!(actor: actor) )
cycles = get_member_cycles(member.id, actor) cycles = get_member_cycles(member.id, actor)
@ -123,23 +124,23 @@ defmodule Mv.MembershipFees.MemberCycleIntegrationTest do
fee_type = create_fee_type(%{interval: :yearly}, actor) fee_type = create_fee_type(%{interval: :yearly}, actor)
# Create member without fee type # Create member without fee type
member = {:ok, member} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "User", last_name: "User",
email: "test#{System.unique_integer([:positive])}@example.com", email: "test#{System.unique_integer([:positive])}@example.com",
join_date: ~D[2023-03-15] join_date: ~D[2023-03-15]
}) },
|> Ash.create!(actor: actor) actor: actor
)
# Verify no cycles yet # Verify no cycles yet
assert get_member_cycles(member.id, actor) == [] assert get_member_cycles(member.id, actor) == []
# Update to assign fee type # Update to assign fee type
member {:ok, member} =
|> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: fee_type.id}) Mv.Membership.update_member(member, %{membership_fee_type_id: fee_type.id}, actor: actor)
|> Ash.update!(actor: actor)
cycles = get_member_cycles(member.id, actor) cycles = get_member_cycles(member.id, actor)
@ -157,15 +158,19 @@ defmodule Mv.MembershipFees.MemberCycleIntegrationTest do
tasks = tasks =
Enum.map(1..5, fn i -> Enum.map(1..5, fn i ->
Task.async(fn -> Task.async(fn ->
Member {:ok, member} =
|> Ash.Changeset.for_create(:create_member, %{ Mv.Membership.create_member(
%{
first_name: "Test#{i}", first_name: "Test#{i}",
last_name: "User#{i}", last_name: "User#{i}",
email: "test#{System.unique_integer([:positive])}@example.com", email: "test#{System.unique_integer([:positive])}@example.com",
join_date: ~D[2023-03-15], join_date: ~D[2023-03-15],
membership_fee_type_id: fee_type.id membership_fee_type_id: fee_type.id
}) },
|> Ash.create!(actor: actor) actor: actor
)
member
end) end)
end) end)
@ -184,16 +189,17 @@ defmodule Mv.MembershipFees.MemberCycleIntegrationTest do
setup_settings(true, actor) setup_settings(true, actor)
fee_type = create_fee_type(%{interval: :yearly}, actor) fee_type = create_fee_type(%{interval: :yearly}, actor)
member = {:ok, member} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "User", last_name: "User",
email: "test#{System.unique_integer([:positive])}@example.com", email: "test#{System.unique_integer([:positive])}@example.com",
join_date: ~D[2023-03-15], join_date: ~D[2023-03-15],
membership_fee_type_id: fee_type.id membership_fee_type_id: fee_type.id
}) },
|> Ash.create!(actor: actor) actor: actor
)
initial_cycles = get_member_cycles(member.id, actor) initial_cycles = get_member_cycles(member.id, actor)
initial_count = length(initial_cycles) initial_count = length(initial_cycles)

View file

@ -37,10 +37,8 @@ defmodule Mv.MembershipFees.MembershipFeeCycleTest do
} }
attrs = Map.merge(default_attrs, attrs) attrs = Map.merge(default_attrs, attrs)
{:ok, member} = Mv.Membership.create_member(attrs, actor: actor)
Member member
|> Ash.Changeset.for_create(:create_member, attrs)
|> Ash.create!(actor: actor)
end end
# Helper to create a cycle # Helper to create a cycle

View file

@ -9,7 +9,7 @@ defmodule Mv.Membership.CustomFieldValuePoliciesTest do
# async: false because we need database commits to be visible across queries # async: false because we need database commits to be visible across queries
use Mv.DataCase, async: false use Mv.DataCase, async: false
alias Mv.Membership.{CustomField, CustomFieldValue, Member} alias Mv.Membership.{CustomField, CustomFieldValue}
alias Mv.Accounts alias Mv.Accounts
alias Mv.Authorization alias Mv.Authorization
@ -62,13 +62,14 @@ defmodule Mv.Membership.CustomFieldValuePoliciesTest do
defp create_linked_member_for_user(user, actor) do defp create_linked_member_for_user(user, actor) do
{:ok, member} = {:ok, member} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Linked", first_name: "Linked",
last_name: "Member", last_name: "Member",
email: "linked#{System.unique_integer([:positive])}@example.com" email: "linked#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create(actor: actor, return_notifications?: false) actor: actor
)
user user
|> Ash.Changeset.for_update(:update, %{}) |> Ash.Changeset.for_update(:update, %{})
@ -80,13 +81,14 @@ defmodule Mv.Membership.CustomFieldValuePoliciesTest do
defp create_unlinked_member(actor) do defp create_unlinked_member(actor) do
{:ok, member} = {:ok, member} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Unlinked", first_name: "Unlinked",
last_name: "Member", last_name: "Member",
email: "unlinked#{System.unique_integer([:positive])}@example.com" email: "unlinked#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create(actor: actor) actor: actor
)
member member
end end

View file

@ -78,13 +78,14 @@ defmodule Mv.Membership.MemberPoliciesTest do
# NOTE: We need to ensure the member is actually persisted to the database # NOTE: We need to ensure the member is actually persisted to the database
# before we try to link it. Ash may delay writes, so we explicitly return the struct. # before we try to link it. Ash may delay writes, so we explicitly return the struct.
{:ok, member} = {:ok, member} =
Membership.Member Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Linked", first_name: "Linked",
last_name: "Member", last_name: "Member",
email: "linked#{System.unique_integer([:positive])}@example.com" email: "linked#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create(actor: admin, return_notifications?: false) actor: admin
)
# Link member to user (User.member_id = member.id) # Link member to user (User.member_id = member.id)
# We use force_change_attribute because the member already exists and we just # We use force_change_attribute because the member already exists and we just
@ -108,13 +109,14 @@ defmodule Mv.Membership.MemberPoliciesTest do
admin = create_admin_user(actor) admin = create_admin_user(actor)
{:ok, member} = {:ok, member} =
Membership.Member Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Unlinked", first_name: "Unlinked",
last_name: "Member", last_name: "Member",
email: "unlinked#{System.unique_integer([:positive])}@example.com" email: "unlinked#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create(actor: admin) actor: admin
)
member member
end end
@ -145,9 +147,7 @@ defmodule Mv.Membership.MemberPoliciesTest do
# Update is allowed via HasPermission check with :linked scope (not via special case) # Update is allowed via HasPermission check with :linked scope (not via special case)
# The special case policy only applies to :read actions # The special case policy only applies to :read actions
{:ok, updated_member} = {:ok, updated_member} =
linked_member Membership.update_member(linked_member, %{first_name: "Updated"}, actor: user)
|> Ash.Changeset.for_update(:update_member, %{first_name: "Updated"})
|> Ash.update(actor: user)
assert updated_member.first_name == "Updated" assert updated_member.first_name == "Updated"
end end
@ -168,11 +168,8 @@ defmodule Mv.Membership.MemberPoliciesTest do
user: user, user: user,
unlinked_member: unlinked_member unlinked_member: unlinked_member
} do } do
assert_raise Ash.Error.Forbidden, fn -> assert {:error, %Ash.Error.Forbidden{}} =
unlinked_member Membership.update_member(unlinked_member, %{first_name: "Updated"}, actor: user)
|> Ash.Changeset.for_update(:update_member, %{first_name: "Updated"})
|> Ash.update!(actor: user)
end
end end
test "list members returns only linked member", %{ test "list members returns only linked member", %{
@ -187,15 +184,15 @@ defmodule Mv.Membership.MemberPoliciesTest do
end end
test "cannot create member (returns forbidden)", %{user: user} do test "cannot create member (returns forbidden)", %{user: user} do
assert_raise Ash.Error.Forbidden, fn -> assert {:error, %Ash.Error.Forbidden{}} =
Membership.Member Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "New", first_name: "New",
last_name: "Member", last_name: "Member",
email: "new#{System.unique_integer([:positive])}@example.com" email: "new#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create!(actor: user) actor: user
end )
end end
test "cannot destroy member (returns forbidden)", %{ test "cannot destroy member (returns forbidden)", %{
@ -245,26 +242,23 @@ defmodule Mv.Membership.MemberPoliciesTest do
end end
test "cannot create member (returns forbidden)", %{user: user} do test "cannot create member (returns forbidden)", %{user: user} do
assert_raise Ash.Error.Forbidden, fn -> assert {:error, %Ash.Error.Forbidden{}} =
Membership.Member Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "New", first_name: "New",
last_name: "Member", last_name: "Member",
email: "new#{System.unique_integer([:positive])}@example.com" email: "new#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create!(actor: user) actor: user
end )
end end
test "cannot update any member (returns forbidden)", %{ test "cannot update any member (returns forbidden)", %{
user: user, user: user,
linked_member: linked_member linked_member: linked_member
} do } do
assert_raise Ash.Error.Forbidden, fn -> assert {:error, %Ash.Error.Forbidden{}} =
linked_member Membership.update_member(linked_member, %{first_name: "Updated"}, actor: user)
|> Ash.Changeset.for_update(:update_member, %{first_name: "Updated"})
|> Ash.update!(actor: user)
end
end end
test "cannot destroy any member (returns forbidden)", %{ test "cannot destroy any member (returns forbidden)", %{
@ -305,22 +299,21 @@ defmodule Mv.Membership.MemberPoliciesTest do
test "can create member", %{user: user} do test "can create member", %{user: user} do
{:ok, member} = {:ok, member} =
Membership.Member Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "New", first_name: "New",
last_name: "Member", last_name: "Member",
email: "new#{System.unique_integer([:positive])}@example.com" email: "new#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create(actor: user) actor: user
)
assert member.first_name == "New" assert member.first_name == "New"
end end
test "can update any member", %{user: user, unlinked_member: unlinked_member} do test "can update any member", %{user: user, unlinked_member: unlinked_member} do
{:ok, updated_member} = {:ok, updated_member} =
unlinked_member Membership.update_member(unlinked_member, %{first_name: "Updated"}, actor: user)
|> Ash.Changeset.for_update(:update_member, %{first_name: "Updated"})
|> Ash.update(actor: user)
assert updated_member.first_name == "Updated" assert updated_member.first_name == "Updated"
end end
@ -363,22 +356,21 @@ defmodule Mv.Membership.MemberPoliciesTest do
test "can create member", %{user: user} do test "can create member", %{user: user} do
{:ok, member} = {:ok, member} =
Membership.Member Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "New", first_name: "New",
last_name: "Member", last_name: "Member",
email: "new#{System.unique_integer([:positive])}@example.com" email: "new#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create(actor: user) actor: user
)
assert member.first_name == "New" assert member.first_name == "New"
end end
test "can update any member", %{user: user, unlinked_member: unlinked_member} do test "can update any member", %{user: user, unlinked_member: unlinked_member} do
{:ok, updated_member} = {:ok, updated_member} =
unlinked_member Membership.update_member(unlinked_member, %{first_name: "Updated"}, actor: user)
|> Ash.Changeset.for_update(:update_member, %{first_name: "Updated"})
|> Ash.update(actor: user)
assert updated_member.first_name == "Updated" assert updated_member.first_name == "Updated"
end end
@ -456,9 +448,7 @@ defmodule Mv.Membership.MemberPoliciesTest do
# Should succeed via HasPermission check (not special case) # Should succeed via HasPermission check (not special case)
{:ok, updated_member} = {:ok, updated_member} =
linked_member Membership.update_member(linked_member, %{first_name: "Updated"}, actor: user)
|> Ash.Changeset.for_update(:update_member, %{first_name: "Updated"})
|> Ash.update(actor: user)
assert updated_member.first_name == "Updated" assert updated_member.first_name == "Updated"
end end

View file

@ -49,10 +49,8 @@ defmodule Mv.MembershipFees.CycleGeneratorEdgeCasesTest do
} }
attrs = Map.merge(default_attrs, attrs) attrs = Map.merge(default_attrs, attrs)
{:ok, member} = Mv.Membership.create_member(attrs, actor: actor)
Member member
|> Ash.Changeset.for_create(:create_member, attrs)
|> Ash.create!(actor: actor)
end end
# Helper to create a member and explicitly generate cycles with a fixed "today" date. # Helper to create a member and explicitly generate cycles with a fixed "today" date.
@ -74,9 +72,12 @@ defmodule Mv.MembershipFees.CycleGeneratorEdgeCasesTest do
# Assign fee type if provided (this will trigger auto-generation with real today) # Assign fee type if provided (this will trigger auto-generation with real today)
member = member =
if fee_type_id do if fee_type_id do
member {:ok, updated} =
|> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: fee_type_id}) Mv.Membership.update_member(member, %{membership_fee_type_id: fee_type_id},
|> Ash.update!(actor: actor) actor: actor
)
updated
else else
member member
end end
@ -130,10 +131,8 @@ defmodule Mv.MembershipFees.CycleGeneratorEdgeCasesTest do
) )
# Assign fee type # Assign fee type
member = {:ok, member} =
member Mv.Membership.update_member(member, %{membership_fee_type_id: fee_type.id}, actor: actor)
|> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: fee_type.id})
|> Ash.update!(actor: actor)
# Explicitly generate cycles with fixed "today" date # Explicitly generate cycles with fixed "today" date
{:ok, _, _} = CycleGenerator.generate_cycles_for_member(member.id, today: today) {:ok, _, _} = CycleGenerator.generate_cycles_for_member(member.id, today: today)
@ -163,10 +162,8 @@ defmodule Mv.MembershipFees.CycleGeneratorEdgeCasesTest do
) )
# Assign fee type # Assign fee type
member = {:ok, member} =
member Mv.Membership.update_member(member, %{membership_fee_type_id: fee_type.id}, actor: actor)
|> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: fee_type.id})
|> Ash.update!(actor: actor)
# Explicitly generate cycles with fixed "today" date # Explicitly generate cycles with fixed "today" date
{:ok, _, _} = CycleGenerator.generate_cycles_for_member(member.id, today: today) {:ok, _, _} = CycleGenerator.generate_cycles_for_member(member.id, today: today)
@ -349,10 +346,8 @@ defmodule Mv.MembershipFees.CycleGeneratorEdgeCasesTest do
|> Ash.create!(actor: actor) |> Ash.create!(actor: actor)
# Now assign fee type # Now assign fee type
member = {:ok, member} =
member Mv.Membership.update_member(member, %{membership_fee_type_id: fee_type.id}, actor: actor)
|> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: fee_type.id})
|> Ash.update!(actor: actor)
# Explicitly generate cycles with fixed "today" date # Explicitly generate cycles with fixed "today" date
today = ~D[2024-06-15] today = ~D[2024-06-15]
@ -616,13 +611,15 @@ defmodule Mv.MembershipFees.CycleGeneratorEdgeCasesTest do
) )
# Now assign fee type (simulating a retroactive assignment) # Now assign fee type (simulating a retroactive assignment)
member = {:ok, member} =
member Mv.Membership.update_member(
|> Ash.Changeset.for_update(:update_member, %{ member,
%{
membership_fee_type_id: fee_type.id, membership_fee_type_id: fee_type.id,
membership_fee_start_date: ~D[2021-01-01] membership_fee_start_date: ~D[2021-01-01]
}) },
|> Ash.update!(actor: actor) actor: actor
)
# Run batch generation with a "today" date after the member left # Run batch generation with a "today" date after the member left
today = ~D[2024-06-15] today = ~D[2024-06-15]

View file

@ -40,10 +40,8 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
} }
attrs = Map.merge(default_attrs, attrs) attrs = Map.merge(default_attrs, attrs)
{:ok, member} = Mv.Membership.create_member(attrs, actor: actor)
Member member
|> Ash.Changeset.for_create(:create_member, attrs)
|> Ash.create!(actor: actor)
end end
# Helper to set up settings with specific include_joining_cycle value # Helper to set up settings with specific include_joining_cycle value
@ -81,8 +79,12 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
# Assign fee type # Assign fee type
member = member =
member member
|> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: fee_type.id}) |> then(fn m ->
|> Ash.update!(actor: actor) {:ok, updated} =
Mv.Membership.update_member(m, %{membership_fee_type_id: fee_type.id}, actor: actor)
updated
end)
# Explicitly generate cycles with fixed "today" date to avoid date dependency # Explicitly generate cycles with fixed "today" date to avoid date dependency
today = ~D[2024-06-15] today = ~D[2024-06-15]
@ -128,8 +130,12 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
# Now assign fee type to member # Now assign fee type to member
member = member =
member member
|> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: fee_type.id}) |> then(fn m ->
|> Ash.update!(actor: actor) {:ok, updated} =
Mv.Membership.update_member(m, %{membership_fee_type_id: fee_type.id}, actor: actor)
updated
end)
# Generate cycles with specific "today" date # Generate cycles with specific "today" date
today = ~D[2024-06-15] today = ~D[2024-06-15]
@ -237,8 +243,12 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
# start from the last existing cycle (2023) and go forward # start from the last existing cycle (2023) and go forward
member = member =
member member
|> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: fee_type.id}) |> then(fn m ->
|> Ash.update!(actor: actor) {:ok, updated} =
Mv.Membership.update_member(m, %{membership_fee_type_id: fee_type.id}, actor: actor)
updated
end)
# Verify gap was NOT filled and new cycles were generated from last existing # Verify gap was NOT filled and new cycles were generated from last existing
all_cycles = get_member_cycles(member.id, actor) all_cycles = get_member_cycles(member.id, actor)

View file

@ -80,21 +80,20 @@ defmodule MvWeb.Helpers.MembershipFeeHelpersTest do
|> Ash.create!(actor: actor) |> Ash.create!(actor: actor)
# Create member without fee type first to avoid auto-generation # Create member without fee type first to avoid auto-generation
member = {:ok, member} =
Mv.Membership.Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "Member", last_name: "Member",
email: "test#{System.unique_integer([:positive])}@example.com", email: "test#{System.unique_integer([:positive])}@example.com",
join_date: ~D[2022-01-01] join_date: ~D[2022-01-01]
}) },
|> Ash.create!(actor: actor) actor: actor
)
# Assign fee type after member creation (this may generate cycles, but we'll create our own) # Assign fee type after member creation (this may generate cycles, but we'll create our own)
member = {:ok, member} =
member Mv.Membership.update_member(member, %{membership_fee_type_id: fee_type.id}, actor: actor)
|> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: fee_type.id})
|> Ash.update!(actor: actor)
# Delete any auto-generated cycles first # Delete any auto-generated cycles first
cycles = cycles =
@ -151,20 +150,19 @@ defmodule MvWeb.Helpers.MembershipFeeHelpersTest do
|> Ash.create!(actor: actor) |> Ash.create!(actor: actor)
# Create member without fee type first # Create member without fee type first
member = {:ok, member} =
Mv.Membership.Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "Member", last_name: "Member",
email: "test#{System.unique_integer([:positive])}@example.com" email: "test#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create!(actor: actor) actor: actor
)
# Assign fee type # Assign fee type
member = {:ok, member} =
member Mv.Membership.update_member(member, %{membership_fee_type_id: fee_type.id}, actor: actor)
|> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: fee_type.id})
|> Ash.update!(actor: actor)
# Delete any auto-generated cycles # Delete any auto-generated cycles
cycles = cycles =
@ -197,21 +195,20 @@ defmodule MvWeb.Helpers.MembershipFeeHelpersTest do
|> Ash.create!(actor: actor) |> Ash.create!(actor: actor)
# Create member without fee type first # Create member without fee type first
member = {:ok, member} =
Mv.Membership.Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "Member", last_name: "Member",
email: "test#{System.unique_integer([:positive])}@example.com", email: "test#{System.unique_integer([:positive])}@example.com",
join_date: ~D[2023-01-01] join_date: ~D[2023-01-01]
}) },
|> Ash.create!(actor: actor) actor: actor
)
# Assign fee type # Assign fee type
member = {:ok, member} =
member Mv.Membership.update_member(member, %{membership_fee_type_id: fee_type.id}, actor: actor)
|> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: fee_type.id})
|> Ash.update!(actor: actor)
# Delete any auto-generated cycles # Delete any auto-generated cycles
cycles = cycles =

View file

@ -41,7 +41,8 @@ defmodule MvWeb.CustomFieldLive.DeletionTest do
user_with_role = Ash.load!(user, :role, domain: Mv.Accounts, actor: system_actor) user_with_role = Ash.load!(user, :role, domain: Mv.Accounts, actor: system_actor)
conn = log_in_user(build_conn(), user_with_role) conn = log_in_user(build_conn(), user_with_role)
# Use English locale so "Delete" link text matches in assertions # Use English locale so "Delete" link text matches in assertions
conn = Plug.Test.init_test_session(conn, Map.put(conn.private.plug_session, "locale", "en")) session = conn.private[:plug_session] || %{}
conn = Plug.Test.init_test_session(conn, Map.put(session, "locale", "en"))
%{conn: conn, user: user_with_role} %{conn: conn, user: user_with_role}
end end
@ -271,17 +272,18 @@ defmodule MvWeb.CustomFieldLive.DeletionTest do
end end
end end
# Helper functions # Helper functions (use code interface so actor is in validation context)
defp create_member do defp create_member do
system_actor = Mv.Helpers.SystemActor.get_system_actor() system_actor = Mv.Helpers.SystemActor.get_system_actor()
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "User#{System.unique_integer([:positive])}", last_name: "User#{System.unique_integer([:positive])}",
email: "test#{System.unique_integer([:positive])}@example.com" email: "test#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create(actor: system_actor) actor: system_actor
)
end end
defp create_custom_field(name, value_type) do defp create_custom_field(name, value_type) do

View file

@ -7,7 +7,6 @@ defmodule MvWeb.MembershipFeeTypeLive.FormTest do
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
alias Mv.MembershipFees.MembershipFeeType alias Mv.MembershipFees.MembershipFeeType
alias Mv.Membership.Member
require Ash.Query require Ash.Query
@ -55,10 +54,8 @@ defmodule MvWeb.MembershipFeeTypeLive.FormTest do
} }
attrs = Map.merge(default_attrs, attrs) attrs = Map.merge(default_attrs, attrs)
{:ok, member} = Mv.Membership.create_member(attrs, actor: system_actor)
Member member
|> Ash.Changeset.for_create(:create_member, attrs)
|> Ash.create!(actor: system_actor)
end end
describe "create form" do describe "create form" do

View file

@ -7,7 +7,6 @@ defmodule MvWeb.MembershipFeeTypeLive.IndexTest do
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
alias Mv.MembershipFees.MembershipFeeType alias Mv.MembershipFees.MembershipFeeType
alias Mv.Membership.Member
require Ash.Query require Ash.Query
@ -31,7 +30,7 @@ defmodule MvWeb.MembershipFeeTypeLive.IndexTest do
end end
# Helper to create a member # Helper to create a member
# Uses admin actor from global setup to ensure authorization # Uses admin actor from global setup to ensure authorization; falls back to system_actor when nil
defp create_member(attrs, actor) do defp create_member(attrs, actor) do
default_attrs = %{ default_attrs = %{
first_name: "Test", first_name: "Test",
@ -40,12 +39,9 @@ defmodule MvWeb.MembershipFeeTypeLive.IndexTest do
} }
attrs = Map.merge(default_attrs, attrs) attrs = Map.merge(default_attrs, attrs)
effective_actor = actor || Mv.Helpers.SystemActor.get_system_actor()
opts = if actor, do: [actor: actor], else: [] {:ok, member} = Mv.Membership.create_member(attrs, actor: effective_actor)
member
Member
|> Ash.Changeset.for_create(:create_member, attrs)
|> Ash.create!(opts)
end end
describe "list display" do describe "list display" do

View file

@ -64,13 +64,10 @@ defmodule MvWeb.UserLive.ShowTest do
# Create member # Create member
{:ok, member} = {:ok, member} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "Alice", last_name: "Smith", email: "alice@example.com"},
first_name: "Alice", actor: system_actor
last_name: "Smith", )
email: "alice@example.com"
})
|> Ash.create(actor: system_actor)
# Create user and link to member # Create user and link to member
user = create_test_user(%{email: "user@example.com"}) user = create_test_user(%{email: "user@example.com"})

View file

@ -6,8 +6,6 @@ defmodule MvWeb.MemberLive.FormErrorHandlingTest do
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
alias Mv.Membership.Member
require Ash.Query require Ash.Query
describe "error handling - flash messages" do describe "error handling - flash messages" do
@ -16,13 +14,14 @@ defmodule MvWeb.MemberLive.FormErrorHandlingTest do
# Create a member with the same email to trigger uniqueness error # Create a member with the same email to trigger uniqueness error
{:ok, _existing_member} = {:ok, _existing_member} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Existing", first_name: "Existing",
last_name: "Member", last_name: "Member",
email: "duplicate@example.com" email: "duplicate@example.com"
}) },
|> Ash.create(actor: system_actor) actor: system_actor
)
conn = conn_with_oidc_user(conn) conn = conn_with_oidc_user(conn)
{:ok, view, _html} = live(conn, "/members/new") {:ok, view, _html} = live(conn, "/members/new")
@ -79,23 +78,21 @@ defmodule MvWeb.MemberLive.FormErrorHandlingTest do
# Create a member to edit # Create a member to edit
{:ok, member} = {:ok, member} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Original", first_name: "Original",
last_name: "Member", last_name: "Member",
email: "original@example.com" email: "original@example.com"
}) },
|> Ash.create(actor: system_actor) actor: system_actor
)
# Create another member with different email # Create another member with different email
{:ok, _other_member} = {:ok, _other_member} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "Other", last_name: "Member", email: "other@example.com"},
first_name: "Other", actor: system_actor
last_name: "Member", )
email: "other@example.com"
})
|> Ash.create(actor: system_actor)
conn = conn_with_oidc_user(conn) conn = conn_with_oidc_user(conn)
{:ok, view, _html} = live(conn, "/members/#{member.id}/edit") {:ok, view, _html} = live(conn, "/members/#{member.id}/edit")

View file

@ -6,7 +6,6 @@ defmodule MvWeb.MemberLive.FormMembershipFeeTypeTest do
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
alias Mv.Membership.Member
alias Mv.MembershipFees.MembershipFeeType alias Mv.MembershipFees.MembershipFeeType
require Ash.Query require Ash.Query
@ -37,10 +36,8 @@ defmodule MvWeb.MemberLive.FormMembershipFeeTypeTest do
} }
attrs = Map.merge(default_attrs, attrs) attrs = Map.merge(default_attrs, attrs)
{:ok, member} = Mv.Membership.create_member(attrs, actor: admin_user)
Member member
|> Ash.Changeset.for_create(:create_member, attrs)
|> Ash.create!(actor: admin_user)
end end
describe "membership fee type dropdown" do describe "membership fee type dropdown" do
@ -129,7 +126,7 @@ defmodule MvWeb.MemberLive.FormMembershipFeeTypeTest do
# Verify member was created with fee type - use admin_user to test permissions # Verify member was created with fee type - use admin_user to test permissions
member = member =
Member Mv.Membership.Member
|> Ash.Query.filter(email == ^form_data["member[email]"]) |> Ash.Query.filter(email == ^form_data["member[email]"])
|> Ash.read_one!(actor: admin_user) |> Ash.read_one!(actor: admin_user)
@ -177,14 +174,15 @@ defmodule MvWeb.MemberLive.FormMembershipFeeTypeTest do
# Create member with fee type 1 and custom field value # Create member with fee type 1 and custom field value
member = member =
Member create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "Member", last_name: "Member",
email: "test#{System.unique_integer([:positive])}@example.com", email: "test#{System.unique_integer([:positive])}@example.com",
membership_fee_type_id: fee_type1.id membership_fee_type_id: fee_type1.id
}) },
|> Ash.create!(actor: admin_user) admin_user
)
# Add custom field value # Add custom field value
Mv.Membership.CustomFieldValue Mv.Membership.CustomFieldValue
@ -222,14 +220,15 @@ defmodule MvWeb.MemberLive.FormMembershipFeeTypeTest do
# Create member with date custom field value # Create member with date custom field value
member = member =
Member create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "Member", last_name: "Member",
email: "test#{System.unique_integer([:positive])}@example.com", email: "test#{System.unique_integer([:positive])}@example.com",
membership_fee_type_id: fee_type.id membership_fee_type_id: fee_type.id
}) },
|> Ash.create!(actor: admin_user) admin_user
)
test_date = ~D[2024-01-15] test_date = ~D[2024-01-15]
@ -269,14 +268,15 @@ defmodule MvWeb.MemberLive.FormMembershipFeeTypeTest do
# Create member with custom field value # Create member with custom field value
member = member =
Member create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "Member", last_name: "Member",
email: "test#{System.unique_integer([:positive])}@example.com", email: "test#{System.unique_integer([:positive])}@example.com",
membership_fee_type_id: fee_type.id membership_fee_type_id: fee_type.id
}) },
|> Ash.create!(actor: admin_user) admin_user
)
# Add custom field value # Add custom field value
_cfv = _cfv =

View file

@ -39,10 +39,8 @@ defmodule MvWeb.MemberLive.Index.MembershipFeeStatusTest do
} }
attrs = Map.merge(default_attrs, attrs) attrs = Map.merge(default_attrs, attrs)
{:ok, member} = Mv.Membership.create_member(attrs, actor: system_actor)
Member member
|> Ash.Changeset.for_create(:create_member, attrs)
|> Ash.create!(actor: system_actor)
end end
# Helper to create a cycle # Helper to create a cycle
@ -106,8 +104,14 @@ defmodule MvWeb.MemberLive.Index.MembershipFeeStatusTest do
# Assign fee type # Assign fee type
member = member =
member member
|> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: fee_type.id}) |> then(fn m ->
|> Ash.update!(actor: system_actor) {:ok, updated} =
Mv.Membership.update_member(m, %{membership_fee_type_id: fee_type.id},
actor: system_actor
)
updated
end)
# Delete any auto-generated cycles # Delete any auto-generated cycles
cycles = cycles =
@ -151,8 +155,14 @@ defmodule MvWeb.MemberLive.Index.MembershipFeeStatusTest do
# Assign fee type # Assign fee type
member = member =
member member
|> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: fee_type.id}) |> then(fn m ->
|> Ash.update!(actor: system_actor) {:ok, updated} =
Mv.Membership.update_member(m, %{membership_fee_type_id: fee_type.id},
actor: system_actor
)
updated
end)
# Delete any auto-generated cycles # Delete any auto-generated cycles
cycles = cycles =
@ -192,8 +202,14 @@ defmodule MvWeb.MemberLive.Index.MembershipFeeStatusTest do
# Assign fee type # Assign fee type
member = member =
member member
|> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: fee_type.id}) |> then(fn m ->
|> Ash.update!(actor: system_actor) {:ok, updated} =
Mv.Membership.update_member(m, %{membership_fee_type_id: fee_type.id},
actor: system_actor
)
updated
end)
# Delete any auto-generated cycles # Delete any auto-generated cycles
cycles = cycles =

View file

@ -11,20 +11,17 @@ defmodule MvWeb.MemberLive.IndexCustomFieldsAccessibilityTest do
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
require Ash.Query require Ash.Query
alias Mv.Membership.{CustomField, CustomFieldValue, Member} alias Mv.Membership.{CustomField, CustomFieldValue}
setup do setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor() system_actor = Mv.Helpers.SystemActor.get_system_actor()
# Create test member # Create test member
{:ok, member} = {:ok, member} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "Alice", last_name: "Anderson", email: "alice@example.com"},
first_name: "Alice", actor: system_actor
last_name: "Anderson", )
email: "alice@example.com"
})
|> Ash.create(actor: system_actor)
# Create custom field with show_in_overview: true # Create custom field with show_in_overview: true
{:ok, field} = {:ok, field} =

View file

@ -14,29 +14,23 @@ defmodule MvWeb.MemberLive.IndexCustomFieldsDisplayTest do
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
require Ash.Query require Ash.Query
alias Mv.Membership.{CustomField, CustomFieldValue, Member} alias Mv.Membership.{CustomField, CustomFieldValue}
setup do setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor() system_actor = Mv.Helpers.SystemActor.get_system_actor()
# Create test members # Create test members
{:ok, member1} = {:ok, member1} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "Alice", last_name: "Anderson", email: "alice@example.com"},
first_name: "Alice", actor: system_actor
last_name: "Anderson", )
email: "alice@example.com"
})
|> Ash.create(actor: system_actor)
{:ok, member2} = {:ok, member2} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "Bob", last_name: "Brown", email: "bob@example.com"},
first_name: "Bob", actor: system_actor
last_name: "Brown", )
email: "bob@example.com"
})
|> Ash.create(actor: system_actor)
# Create custom fields # Create custom fields
{:ok, field_show_string} = {:ok, field_show_string} =

View file

@ -10,7 +10,7 @@ defmodule MvWeb.MemberLive.IndexCustomFieldsEdgeCasesTest do
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
require Ash.Query require Ash.Query
alias Mv.Membership.{CustomField, Member} alias Mv.Membership.CustomField
@tag :slow @tag :slow
test "displays custom field column even when no members have values", %{conn: conn} do test "displays custom field column even when no members have values", %{conn: conn} do
@ -18,22 +18,16 @@ defmodule MvWeb.MemberLive.IndexCustomFieldsEdgeCasesTest do
# Create test members without custom field values # Create test members without custom field values
{:ok, _member1} = {:ok, _member1} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "Alice", last_name: "Anderson", email: "alice@example.com"},
first_name: "Alice", actor: system_actor
last_name: "Anderson", )
email: "alice@example.com"
})
|> Ash.create(actor: system_actor)
{:ok, _member2} = {:ok, _member2} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "Bob", last_name: "Brown", email: "bob@example.com"},
first_name: "Bob", actor: system_actor
last_name: "Brown", )
email: "bob@example.com"
})
|> Ash.create(actor: system_actor)
# Create custom field with show_in_overview: true but no values # Create custom field with show_in_overview: true but no values
{:ok, field} = {:ok, field} =
@ -58,13 +52,10 @@ defmodule MvWeb.MemberLive.IndexCustomFieldsEdgeCasesTest do
# Create test member # Create test member
{:ok, member} = {:ok, member} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "Alice", last_name: "Anderson", email: "alice@example.com"},
first_name: "Alice", actor: system_actor
last_name: "Anderson", )
email: "alice@example.com"
})
|> Ash.create(actor: system_actor)
# Create custom field # Create custom field
{:ok, field} = {:ok, field} =
@ -102,13 +93,10 @@ defmodule MvWeb.MemberLive.IndexCustomFieldsEdgeCasesTest do
# Create test member # Create test member
{:ok, member} = {:ok, member} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "Alice", last_name: "Anderson", email: "alice@example.com"},
first_name: "Alice", actor: system_actor
last_name: "Anderson", )
email: "alice@example.com"
})
|> Ash.create(actor: system_actor)
# Create multiple custom fields with show_in_overview: true # Create multiple custom fields with show_in_overview: true
{:ok, field1} = {:ok, field1} =

View file

@ -13,38 +13,29 @@ defmodule MvWeb.MemberLive.IndexCustomFieldsSortingTest do
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
require Ash.Query require Ash.Query
alias Mv.Membership.{CustomField, CustomFieldValue, Member} alias Mv.Membership.{CustomField, CustomFieldValue}
setup do setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor() system_actor = Mv.Helpers.SystemActor.get_system_actor()
# Create test members # Create test members
{:ok, member1} = {:ok, member1} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "Alice", last_name: "Anderson", email: "alice@example.com"},
first_name: "Alice", actor: system_actor
last_name: "Anderson", )
email: "alice@example.com"
})
|> Ash.create(actor: system_actor)
{:ok, member2} = {:ok, member2} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "Bob", last_name: "Brown", email: "bob@example.com"},
first_name: "Bob", actor: system_actor
last_name: "Brown", )
email: "bob@example.com"
})
|> Ash.create(actor: system_actor)
{:ok, member3} = {:ok, member3} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "Charlie", last_name: "Clark", email: "charlie@example.com"},
first_name: "Charlie", actor: system_actor
last_name: "Clark", )
email: "charlie@example.com"
})
|> Ash.create(actor: system_actor)
# Create custom field with show_in_overview: true # Create custom field with show_in_overview: true
{:ok, field_string} = {:ok, field_string} =
@ -242,40 +233,28 @@ defmodule MvWeb.MemberLive.IndexCustomFieldsSortingTest do
# Create additional members with NULL and empty string values # Create additional members with NULL and empty string values
{:ok, member_with_value} = {:ok, member_with_value} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "WithValue", last_name: "Test", email: "withvalue@example.com"},
first_name: "WithValue", actor: system_actor
last_name: "Test", )
email: "withvalue@example.com"
})
|> Ash.create(actor: system_actor)
{:ok, member_with_empty} = {:ok, member_with_empty} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "WithEmpty", last_name: "Test", email: "withempty@example.com"},
first_name: "WithEmpty", actor: system_actor
last_name: "Test", )
email: "withempty@example.com"
})
|> Ash.create(actor: system_actor)
{:ok, member_with_null} = {:ok, member_with_null} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "WithNull", last_name: "Test", email: "withnull@example.com"},
first_name: "WithNull", actor: system_actor
last_name: "Test", )
email: "withnull@example.com"
})
|> Ash.create(actor: system_actor)
{:ok, member_with_another_value} = {:ok, member_with_another_value} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "AnotherValue", last_name: "Test", email: "another@example.com"},
first_name: "AnotherValue", actor: system_actor
last_name: "Test", )
email: "another@example.com"
})
|> Ash.create(actor: system_actor)
# Create custom field # Create custom field
{:ok, field} = {:ok, field} =
@ -355,40 +334,28 @@ defmodule MvWeb.MemberLive.IndexCustomFieldsSortingTest do
# Create additional members with NULL and empty string values # Create additional members with NULL and empty string values
{:ok, member_with_value} = {:ok, member_with_value} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "WithValue", last_name: "Test", email: "withvalue@example.com"},
first_name: "WithValue", actor: system_actor
last_name: "Test", )
email: "withvalue@example.com"
})
|> Ash.create(actor: system_actor)
{:ok, member_with_empty} = {:ok, member_with_empty} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "WithEmpty", last_name: "Test", email: "withempty@example.com"},
first_name: "WithEmpty", actor: system_actor
last_name: "Test", )
email: "withempty@example.com"
})
|> Ash.create(actor: system_actor)
{:ok, member_with_null} = {:ok, member_with_null} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "WithNull", last_name: "Test", email: "withnull@example.com"},
first_name: "WithNull", actor: system_actor
last_name: "Test", )
email: "withnull@example.com"
})
|> Ash.create(actor: system_actor)
{:ok, member_with_another_value} = {:ok, member_with_another_value} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "AnotherValue", last_name: "Test", email: "another@example.com"},
first_name: "AnotherValue", actor: system_actor
last_name: "Test", )
email: "another@example.com"
})
|> Ash.create(actor: system_actor)
# Create custom field # Create custom field
{:ok, field} = {:ok, field} =

View file

@ -16,33 +16,35 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
require Ash.Query require Ash.Query
alias Mv.Membership.{CustomField, CustomFieldValue, Member} alias Mv.Membership.{CustomField, CustomFieldValue}
setup do setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor() system_actor = Mv.Helpers.SystemActor.get_system_actor()
# Create test members # Create test members
{:ok, member1} = {:ok, member1} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Alice", first_name: "Alice",
last_name: "Anderson", last_name: "Anderson",
email: "alice@example.com", email: "alice@example.com",
street: "Main St", street: "Main St",
city: "Berlin" city: "Berlin"
}) },
|> Ash.create(actor: system_actor) actor: system_actor
)
{:ok, member2} = {:ok, member2} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Bob", first_name: "Bob",
last_name: "Brown", last_name: "Brown",
email: "bob@example.com", email: "bob@example.com",
street: "Second St", street: "Second St",
city: "Hamburg" city: "Hamburg"
}) },
|> Ash.create(actor: system_actor) actor: system_actor
)
# Create custom field # Create custom field
{:ok, custom_field} = {:ok, custom_field} =

View file

@ -3,14 +3,12 @@ defmodule MvWeb.MemberLive.IndexMemberFieldsDisplayTest do
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
require Ash.Query require Ash.Query
alias Mv.Membership.Member
setup do setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor() system_actor = Mv.Helpers.SystemActor.get_system_actor()
{:ok, member1} = {:ok, member1} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Alice", first_name: "Alice",
last_name: "Anderson", last_name: "Anderson",
email: "alice@example.com", email: "alice@example.com",
@ -19,17 +17,15 @@ defmodule MvWeb.MemberLive.IndexMemberFieldsDisplayTest do
postal_code: "12345", postal_code: "12345",
city: "Berlin", city: "Berlin",
join_date: ~D[2020-01-15] join_date: ~D[2020-01-15]
}) },
|> Ash.create(actor: system_actor) actor: system_actor
)
{:ok, member2} = {:ok, member2} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "Bob", last_name: "Brown", email: "bob@example.com"},
first_name: "Bob", actor: system_actor
last_name: "Brown", )
email: "bob@example.com"
})
|> Ash.create(actor: system_actor)
%{ %{
member1: member1, member1: member1,

View file

@ -6,7 +6,6 @@ defmodule MvWeb.MemberLive.IndexMembershipFeeStatusTest do
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
alias Mv.Membership.Member
alias Mv.MembershipFees.MembershipFeeType alias Mv.MembershipFees.MembershipFeeType
alias Mv.MembershipFees.MembershipFeeCycle alias Mv.MembershipFees.MembershipFeeCycle
@ -40,10 +39,8 @@ defmodule MvWeb.MemberLive.IndexMembershipFeeStatusTest do
} }
attrs = Map.merge(default_attrs, attrs) attrs = Map.merge(default_attrs, attrs)
{:ok, member} = Mv.Membership.create_member(attrs, actor: system_actor)
Member member
|> Ash.Changeset.for_create(:create_member, attrs)
|> Ash.create!(actor: system_actor)
end end
# Helper to create a cycle # Helper to create a cycle

View file

@ -531,10 +531,8 @@ defmodule MvWeb.MemberLive.IndexTest do
} }
attrs = Map.merge(default_attrs, attrs) attrs = Map.merge(default_attrs, attrs)
{:ok, member} = Mv.Membership.create_member(attrs, actor: actor)
Mv.Membership.Member member
|> Ash.Changeset.for_create(:create_member, attrs)
|> Ash.create!(actor: actor)
end end
test "filter shows only members with paid status in last cycle", %{conn: conn} do test "filter shows only members with paid status in last cycle", %{conn: conn} do
@ -1127,18 +1125,15 @@ defmodule MvWeb.MemberLive.IndexTest do
# Helper to create a member with a boolean custom field value # Helper to create a member with a boolean custom field value
defp create_member_with_boolean_value(member_attrs, custom_field, value, actor) do defp create_member_with_boolean_value(member_attrs, custom_field, value, actor) do
{:ok, member} = attrs =
Mv.Membership.Member
|> Ash.Changeset.for_create(
:create_member,
%{ %{
first_name: "Test", first_name: "Test",
last_name: "Member", last_name: "Member",
email: "test.member.#{System.unique_integer([:positive])}@example.com" email: "test.member.#{System.unique_integer([:positive])}@example.com"
} }
|> Map.merge(member_attrs) |> Map.merge(member_attrs)
)
|> Ash.create(actor: actor) {:ok, member} = Mv.Membership.create_member(attrs, actor: actor)
{:ok, _cfv} = {:ok, _cfv} =
Mv.Membership.CustomFieldValue Mv.Membership.CustomFieldValue
@ -1182,13 +1177,14 @@ defmodule MvWeb.MemberLive.IndexTest do
boolean_field = create_boolean_custom_field() boolean_field = create_boolean_custom_field()
{:ok, member} = {:ok, member} =
Mv.Membership.Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "Member", last_name: "Member",
email: "test.member.#{System.unique_integer([:positive])}@example.com" email: "test.member.#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create(actor: system_actor) actor: system_actor
)
# Create CustomFieldValue with map format (Ash expects _union_type and _union_value) # Create CustomFieldValue with map format (Ash expects _union_type and _union_value)
{:ok, _cfv} = {:ok, _cfv} =
@ -1215,13 +1211,14 @@ defmodule MvWeb.MemberLive.IndexTest do
boolean_field = create_boolean_custom_field() boolean_field = create_boolean_custom_field()
{:ok, member} = {:ok, member} =
Mv.Membership.Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "Member", last_name: "Member",
email: "test.member.#{System.unique_integer([:positive])}@example.com" email: "test.member.#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create(actor: system_actor) actor: system_actor
)
# Member has no custom field value for this field # Member has no custom field value for this field
member = member |> Ash.load!(:custom_field_values, actor: system_actor) member = member |> Ash.load!(:custom_field_values, actor: system_actor)
@ -1238,13 +1235,14 @@ defmodule MvWeb.MemberLive.IndexTest do
boolean_field = create_boolean_custom_field() boolean_field = create_boolean_custom_field()
{:ok, member} = {:ok, member} =
Mv.Membership.Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "Member", last_name: "Member",
email: "test.member.#{System.unique_integer([:positive])}@example.com" email: "test.member.#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create(actor: system_actor) actor: system_actor
)
# Create CustomFieldValue with nil value (edge case) # Create CustomFieldValue with nil value (edge case)
{:ok, _cfv} = {:ok, _cfv} =
@ -1271,13 +1269,14 @@ defmodule MvWeb.MemberLive.IndexTest do
boolean_field = create_boolean_custom_field() boolean_field = create_boolean_custom_field()
{:ok, member} = {:ok, member} =
Mv.Membership.Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Test", first_name: "Test",
last_name: "Member", last_name: "Member",
email: "test.member.#{System.unique_integer([:positive])}@example.com" email: "test.member.#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create(actor: system_actor) actor: system_actor
)
# Create string custom field value (not boolean) # Create string custom field value (not boolean)
{:ok, _cfv} = {:ok, _cfv} =
@ -1320,13 +1319,14 @@ defmodule MvWeb.MemberLive.IndexTest do
) )
{:ok, member_without_value} = {:ok, member_without_value} =
Mv.Membership.Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "NoValue", first_name: "NoValue",
last_name: "Member", last_name: "Member",
email: "novalue.member.#{System.unique_integer([:positive])}@example.com" email: "novalue.member.#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create(actor: system_actor) actor: system_actor
)
member_without_value = member_without_value =
member_without_value |> Ash.load!(:custom_field_values, actor: system_actor) member_without_value |> Ash.load!(:custom_field_values, actor: system_actor)
@ -1370,13 +1370,14 @@ defmodule MvWeb.MemberLive.IndexTest do
) )
{:ok, member_without_value} = {:ok, member_without_value} =
Mv.Membership.Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "NoValue", first_name: "NoValue",
last_name: "Member", last_name: "Member",
email: "novalue.member.#{System.unique_integer([:positive])}@example.com" email: "novalue.member.#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create(actor: system_actor) actor: system_actor
)
member_without_value = member_without_value =
member_without_value |> Ash.load!(:custom_field_values, actor: system_actor) member_without_value |> Ash.load!(:custom_field_values, actor: system_actor)
@ -1447,13 +1448,14 @@ defmodule MvWeb.MemberLive.IndexTest do
# Member with both fields = true # Member with both fields = true
{:ok, member_both_true} = {:ok, member_both_true} =
Mv.Membership.Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "BothTrue", first_name: "BothTrue",
last_name: "Member", last_name: "Member",
email: "bothtrue.member.#{System.unique_integer([:positive])}@example.com" email: "bothtrue.member.#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create(actor: system_actor) actor: system_actor
)
{:ok, _cfv1} = {:ok, _cfv1} =
Mv.Membership.CustomFieldValue Mv.Membership.CustomFieldValue
@ -1477,13 +1479,14 @@ defmodule MvWeb.MemberLive.IndexTest do
# Member with field1 = true, field2 = false # Member with field1 = true, field2 = false
{:ok, member_mixed} = {:ok, member_mixed} =
Mv.Membership.Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "Mixed", first_name: "Mixed",
last_name: "Member", last_name: "Member",
email: "mixed.member.#{System.unique_integer([:positive])}@example.com" email: "mixed.member.#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create(actor: system_actor) actor: system_actor
)
{:ok, _cfv3} = {:ok, _cfv3} =
Mv.Membership.CustomFieldValue Mv.Membership.CustomFieldValue
@ -1580,13 +1583,14 @@ defmodule MvWeb.MemberLive.IndexTest do
) )
{:ok, _member_without_value} = {:ok, _member_without_value} =
Mv.Membership.Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "NoValue", first_name: "NoValue",
last_name: "Member", last_name: "Member",
email: "novalue.member.#{System.unique_integer([:positive])}@example.com" email: "novalue.member.#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create(actor: system_actor) actor: system_actor
)
# Test true filter # Test true filter
{:ok, _view, html_true} = {:ok, _view, html_true} =
@ -1615,14 +1619,15 @@ defmodule MvWeb.MemberLive.IndexTest do
# Member with true boolean value and paid status # Member with true boolean value and paid status
{:ok, member_paid_true} = {:ok, member_paid_true} =
Mv.Membership.Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "PaidTrue", first_name: "PaidTrue",
last_name: "Member", last_name: "Member",
email: "paidtrue.member.#{System.unique_integer([:positive])}@example.com", email: "paidtrue.member.#{System.unique_integer([:positive])}@example.com",
membership_fee_type_id: fee_type.id membership_fee_type_id: fee_type.id
}) },
|> Ash.create(actor: system_actor) actor: system_actor
)
{:ok, _cfv} = {:ok, _cfv} =
Mv.Membership.CustomFieldValue Mv.Membership.CustomFieldValue
@ -1642,14 +1647,15 @@ defmodule MvWeb.MemberLive.IndexTest do
# Member with true boolean value but unpaid status # Member with true boolean value but unpaid status
{:ok, member_unpaid_true} = {:ok, member_unpaid_true} =
Mv.Membership.Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "UnpaidTrue", first_name: "UnpaidTrue",
last_name: "Member", last_name: "Member",
email: "unpaidtrue.member.#{System.unique_integer([:positive])}@example.com", email: "unpaidtrue.member.#{System.unique_integer([:positive])}@example.com",
membership_fee_type_id: fee_type.id membership_fee_type_id: fee_type.id
}) },
|> Ash.create(actor: system_actor) actor: system_actor
)
{:ok, _cfv2} = {:ok, _cfv2} =
Mv.Membership.CustomFieldValue Mv.Membership.CustomFieldValue
@ -1730,13 +1736,14 @@ defmodule MvWeb.MemberLive.IndexTest do
) )
{:ok, _member_without_value} = {:ok, _member_without_value} =
Mv.Membership.Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{
first_name: "NoValue", first_name: "NoValue",
last_name: "Member", last_name: "Member",
email: "novalue.member.#{System.unique_integer([:positive])}@example.com" email: "novalue.member.#{System.unique_integer([:positive])}@example.com"
}) },
|> Ash.create(actor: system_actor) actor: system_actor
)
# Test that filter works even though field is not visible in overview # Test that filter works even though field is not visible in overview
{:ok, _view, html_true} = {:ok, _view, html_true} =

View file

@ -6,7 +6,6 @@ defmodule MvWeb.MemberLive.MembershipFeeIntegrationTest do
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
alias Mv.Membership.Member
alias Mv.MembershipFees.MembershipFeeType alias Mv.MembershipFees.MembershipFeeType
alias Mv.MembershipFees.MembershipFeeCycle alias Mv.MembershipFees.MembershipFeeCycle
@ -40,10 +39,8 @@ defmodule MvWeb.MemberLive.MembershipFeeIntegrationTest do
} }
attrs = Map.merge(default_attrs, attrs) attrs = Map.merge(default_attrs, attrs)
{:ok, member} = Mv.Membership.create_member(attrs, actor: system_actor)
Member member
|> Ash.Changeset.for_create(:create_member, attrs)
|> Ash.create!(actor: system_actor)
end end
describe "end-to-end workflows" do describe "end-to-end workflows" do
@ -152,7 +149,7 @@ defmodule MvWeb.MemberLive.MembershipFeeIntegrationTest do
system_actor = Mv.Helpers.SystemActor.get_system_actor() system_actor = Mv.Helpers.SystemActor.get_system_actor()
member = member =
Member Mv.Membership.Member
|> Ash.Query.filter(email == ^form_data["member[email]"]) |> Ash.Query.filter(email == ^form_data["member[email]"])
|> Ash.read_one!(actor: system_actor) |> Ash.read_one!(actor: system_actor)

View file

@ -6,7 +6,6 @@ defmodule MvWeb.MemberLive.ShowMembershipFeesTest do
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
alias Mv.Membership.Member
alias Mv.MembershipFees.MembershipFeeType alias Mv.MembershipFees.MembershipFeeType
alias Mv.MembershipFees.MembershipFeeCycle alias Mv.MembershipFees.MembershipFeeCycle
@ -40,10 +39,8 @@ defmodule MvWeb.MemberLive.ShowMembershipFeesTest do
} }
attrs = Map.merge(default_attrs, attrs) attrs = Map.merge(default_attrs, attrs)
{:ok, member} = Mv.Membership.create_member(attrs, actor: system_actor)
Member member
|> Ash.Changeset.for_create(:create_member, attrs)
|> Ash.create!(actor: system_actor)
end end
# Helper to create a cycle # Helper to create a cycle

View file

@ -18,20 +18,17 @@ defmodule MvWeb.MemberLive.ShowTest do
require Ash.Query require Ash.Query
use Gettext, backend: MvWeb.Gettext use Gettext, backend: MvWeb.Gettext
alias Mv.Membership.{CustomField, CustomFieldValue, Member} alias Mv.Membership.{CustomField, CustomFieldValue}
setup do setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor() system_actor = Mv.Helpers.SystemActor.get_system_actor()
# Create test member # Create test member
{:ok, member} = {:ok, member} =
Member Mv.Membership.create_member(
|> Ash.Changeset.for_create(:create_member, %{ %{first_name: "Alice", last_name: "Anderson", email: "alice@example.com"},
first_name: "Alice", actor: system_actor
last_name: "Anderson", )
email: "alice@example.com"
})
|> Ash.create(actor: system_actor)
%{member: member, actor: system_actor} %{member: member, actor: system_actor}
end end