This commit adds actor: system_actor to all Ash operations in tests that require authorization.
296 lines
8.5 KiB
Elixir
296 lines
8.5 KiB
Elixir
defmodule Mv.MembershipFees.ForeignKeyTest do
|
|
@moduledoc """
|
|
Tests for foreign key behaviors (CASCADE and RESTRICT).
|
|
"""
|
|
use Mv.DataCase, async: true
|
|
|
|
alias Mv.MembershipFees.MembershipFeeCycle
|
|
alias Mv.MembershipFees.MembershipFeeType
|
|
alias Mv.Membership.Member
|
|
|
|
setup do
|
|
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
|
%{actor: system_actor}
|
|
end
|
|
|
|
describe "CASCADE behavior" do
|
|
test "deleting member deletes associated membership_fee_cycles", %{actor: actor} do
|
|
# Create member
|
|
{:ok, member} =
|
|
Ash.create(
|
|
Member,
|
|
%{
|
|
first_name: "Cascade",
|
|
last_name: "Test",
|
|
email: "cascade.test.#{System.unique_integer([:positive])}@example.com"
|
|
},
|
|
actor: actor
|
|
)
|
|
|
|
# Create fee type
|
|
{:ok, fee_type} =
|
|
Ash.create(
|
|
MembershipFeeType,
|
|
%{
|
|
name: "Cascade Test Fee #{System.unique_integer([:positive])}",
|
|
amount: Decimal.new("100.00"),
|
|
interval: :monthly
|
|
},
|
|
actor: actor
|
|
)
|
|
|
|
# Create multiple cycles for this member
|
|
{:ok, cycle1} =
|
|
Ash.create(
|
|
MembershipFeeCycle,
|
|
%{
|
|
cycle_start: ~D[2025-01-01],
|
|
amount: Decimal.new("100.00"),
|
|
member_id: member.id,
|
|
membership_fee_type_id: fee_type.id
|
|
},
|
|
actor: actor
|
|
)
|
|
|
|
{:ok, cycle2} =
|
|
Ash.create(
|
|
MembershipFeeCycle,
|
|
%{
|
|
cycle_start: ~D[2025-02-01],
|
|
amount: Decimal.new("100.00"),
|
|
member_id: member.id,
|
|
membership_fee_type_id: fee_type.id
|
|
},
|
|
actor: actor
|
|
)
|
|
|
|
# Verify cycles exist
|
|
assert {:ok, _} = Ash.get(MembershipFeeCycle, cycle1.id, actor: actor)
|
|
assert {:ok, _} = Ash.get(MembershipFeeCycle, cycle2.id, actor: actor)
|
|
|
|
# Delete member
|
|
assert :ok = Ash.destroy(member, actor: actor)
|
|
|
|
# Verify cycles are also deleted (CASCADE)
|
|
# NotFound is wrapped in Ash.Error.Invalid
|
|
assert {:error, %Ash.Error.Invalid{}} = Ash.get(MembershipFeeCycle, cycle1.id, actor: actor)
|
|
assert {:error, %Ash.Error.Invalid{}} = Ash.get(MembershipFeeCycle, cycle2.id, actor: actor)
|
|
end
|
|
end
|
|
|
|
describe "RESTRICT behavior" do
|
|
test "cannot delete membership_fee_type if cycles reference it", %{actor: actor} do
|
|
# Create member
|
|
{:ok, member} =
|
|
Ash.create(
|
|
Member,
|
|
%{
|
|
first_name: "Restrict",
|
|
last_name: "Test",
|
|
email: "restrict.test.#{System.unique_integer([:positive])}@example.com"
|
|
},
|
|
actor: actor
|
|
)
|
|
|
|
# Create fee type
|
|
{:ok, fee_type} =
|
|
Ash.create(
|
|
MembershipFeeType,
|
|
%{
|
|
name: "Restrict Test Fee #{System.unique_integer([:positive])}",
|
|
amount: Decimal.new("100.00"),
|
|
interval: :monthly
|
|
},
|
|
actor: actor
|
|
)
|
|
|
|
# Create a cycle referencing this fee type
|
|
{:ok, _cycle} =
|
|
Ash.create(
|
|
MembershipFeeCycle,
|
|
%{
|
|
cycle_start: ~D[2025-01-01],
|
|
amount: Decimal.new("100.00"),
|
|
member_id: member.id,
|
|
membership_fee_type_id: fee_type.id
|
|
},
|
|
actor: actor
|
|
)
|
|
|
|
# Try to delete fee type - should fail due to RESTRICT
|
|
assert {:error, error} = Ash.destroy(fee_type, actor: actor)
|
|
|
|
# Check that it's a foreign key violation error
|
|
assert is_struct(error, Ash.Error.Invalid) or is_struct(error, Ash.Error.Unknown)
|
|
end
|
|
|
|
test "can delete membership_fee_type if no cycles reference it", %{actor: actor} do
|
|
# Create fee type without any cycles
|
|
{:ok, fee_type} =
|
|
Ash.create(
|
|
MembershipFeeType,
|
|
%{
|
|
name: "Deletable Fee #{System.unique_integer([:positive])}",
|
|
amount: Decimal.new("100.00"),
|
|
interval: :monthly
|
|
},
|
|
actor: actor
|
|
)
|
|
|
|
# Should be able to delete
|
|
assert :ok = Ash.destroy(fee_type, actor: actor)
|
|
|
|
# Verify it's gone (NotFound is wrapped in Ash.Error.Invalid)
|
|
assert {:error, %Ash.Error.Invalid{}} =
|
|
Ash.get(MembershipFeeType, fee_type.id, actor: actor)
|
|
end
|
|
|
|
test "cannot delete membership_fee_type if members reference it", %{actor: actor} do
|
|
# Create fee type
|
|
{:ok, fee_type} =
|
|
Ash.create(
|
|
MembershipFeeType,
|
|
%{
|
|
name: "Member Ref Fee #{System.unique_integer([:positive])}",
|
|
amount: Decimal.new("100.00"),
|
|
interval: :monthly
|
|
},
|
|
actor: actor
|
|
)
|
|
|
|
# Create member with this fee type
|
|
{:ok, _member} =
|
|
Ash.create(
|
|
Member,
|
|
%{
|
|
first_name: "FeeType",
|
|
last_name: "Reference",
|
|
email: "feetype.ref.#{System.unique_integer([:positive])}@example.com",
|
|
membership_fee_type_id: fee_type.id
|
|
},
|
|
actor: actor
|
|
)
|
|
|
|
# Try to delete fee type - should fail due to RESTRICT
|
|
assert {:error, error} = Ash.destroy(fee_type, actor: actor)
|
|
assert is_struct(error, Ash.Error.Invalid) or is_struct(error, Ash.Error.Unknown)
|
|
end
|
|
end
|
|
|
|
describe "member extensions" do
|
|
test "member can be created with membership_fee_type_id", %{actor: actor} do
|
|
# Create fee type first
|
|
{:ok, fee_type} =
|
|
Ash.create(
|
|
MembershipFeeType,
|
|
%{
|
|
name: "Create Test Fee #{System.unique_integer([:positive])}",
|
|
amount: Decimal.new("100.00"),
|
|
interval: :yearly
|
|
},
|
|
actor: actor
|
|
)
|
|
|
|
# Create member with fee type
|
|
{:ok, member} =
|
|
Ash.create(
|
|
Member,
|
|
%{
|
|
first_name: "With",
|
|
last_name: "FeeType",
|
|
email: "with.feetype.#{System.unique_integer([:positive])}@example.com",
|
|
membership_fee_type_id: fee_type.id
|
|
},
|
|
actor: actor
|
|
)
|
|
|
|
assert member.membership_fee_type_id == fee_type.id
|
|
end
|
|
|
|
test "member can be created with membership_fee_start_date", %{actor: actor} do
|
|
{:ok, member} =
|
|
Ash.create(
|
|
Member,
|
|
%{
|
|
first_name: "With",
|
|
last_name: "StartDate",
|
|
email: "with.startdate.#{System.unique_integer([:positive])}@example.com",
|
|
membership_fee_start_date: ~D[2025-01-01]
|
|
},
|
|
actor: actor
|
|
)
|
|
|
|
assert member.membership_fee_start_date == ~D[2025-01-01]
|
|
end
|
|
|
|
test "member can be created without membership fee fields", %{actor: actor} do
|
|
{:ok, member} =
|
|
Ash.create(
|
|
Member,
|
|
%{
|
|
first_name: "No",
|
|
last_name: "FeeFields",
|
|
email: "no.feefields.#{System.unique_integer([:positive])}@example.com"
|
|
},
|
|
actor: actor
|
|
)
|
|
|
|
assert member.membership_fee_type_id == nil
|
|
assert member.membership_fee_start_date == nil
|
|
end
|
|
|
|
test "member can be updated with membership_fee_type_id", %{actor: actor} do
|
|
# Create fee type
|
|
{:ok, fee_type} =
|
|
Ash.create(
|
|
MembershipFeeType,
|
|
%{
|
|
name: "Update Test Fee #{System.unique_integer([:positive])}",
|
|
amount: Decimal.new("100.00"),
|
|
interval: :yearly
|
|
},
|
|
actor: actor
|
|
)
|
|
|
|
# Create member without fee type
|
|
{:ok, member} =
|
|
Ash.create(
|
|
Member,
|
|
%{
|
|
first_name: "Update",
|
|
last_name: "Test",
|
|
email: "update.test.#{System.unique_integer([:positive])}@example.com"
|
|
},
|
|
actor: actor
|
|
)
|
|
|
|
assert member.membership_fee_type_id == nil
|
|
|
|
# Update member with fee type
|
|
{:ok, updated_member} =
|
|
Ash.update(member, %{membership_fee_type_id: fee_type.id}, actor: actor)
|
|
|
|
assert updated_member.membership_fee_type_id == fee_type.id
|
|
end
|
|
|
|
test "member can be updated with membership_fee_start_date", %{actor: actor} do
|
|
{:ok, member} =
|
|
Ash.create(
|
|
Member,
|
|
%{
|
|
first_name: "Start",
|
|
last_name: "Date",
|
|
email: "start.date.#{System.unique_integer([:positive])}@example.com"
|
|
},
|
|
actor: actor
|
|
)
|
|
|
|
assert member.membership_fee_start_date == nil
|
|
|
|
{:ok, updated_member} =
|
|
Ash.update(member, %{membership_fee_start_date: ~D[2025-06-01]}, actor: actor)
|
|
|
|
assert updated_member.membership_fee_start_date == ~D[2025-06-01]
|
|
end
|
|
end
|
|
end
|