Add actor parameter to all tests requiring authorization

This commit adds actor: system_actor to all Ash operations in tests that
require authorization.
This commit is contained in:
Moritz 2026-01-23 20:00:24 +01:00
parent 686f69c9e9
commit 0f48a9b15a
Signed by: moritz
GPG key ID: 1020A035E5DD0824
75 changed files with 4686 additions and 2859 deletions

View file

@ -11,8 +11,13 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
require Ash.Query
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
%{actor: system_actor}
end
# Helper to create a membership fee type
defp create_fee_type(attrs) do
defp create_fee_type(attrs, actor) do
default_attrs = %{
name: "Test Fee Type #{System.unique_integer([:positive])}",
amount: Decimal.new("50.00"),
@ -23,11 +28,11 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
MembershipFeeType
|> Ash.Changeset.for_create(:create, attrs)
|> Ash.create!()
|> Ash.create!(actor: actor)
end
# Helper to create a member without triggering cycle generation
defp create_member_without_cycles(attrs) do
defp create_member_without_cycles(attrs, actor) do
default_attrs = %{
first_name: "Test",
last_name: "User",
@ -38,50 +43,53 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
Member
|> Ash.Changeset.for_create(:create_member, attrs)
|> Ash.create!()
|> Ash.create!(actor: actor)
end
# Helper to set up settings with specific include_joining_cycle value
defp setup_settings(include_joining_cycle) do
defp setup_settings(include_joining_cycle, actor) do
{:ok, settings} = Mv.Membership.get_settings()
settings
|> Ash.Changeset.for_update(:update, %{include_joining_cycle: include_joining_cycle})
|> Ash.update!()
|> Ash.update!(actor: actor)
end
# Helper to get cycles for a member
defp get_member_cycles(member_id) do
defp get_member_cycles(member_id, actor) do
MembershipFeeCycle
|> Ash.Query.filter(member_id == ^member_id)
|> Ash.Query.sort(cycle_start: :asc)
|> Ash.read!()
|> Ash.read!(actor: actor)
end
describe "generate_cycles_for_member/2" do
test "generates cycles from start date to today" do
setup_settings(true)
fee_type = create_fee_type(%{interval: :yearly})
test "generates cycles from start date to today", %{actor: actor} do
setup_settings(true, actor)
fee_type = create_fee_type(%{interval: :yearly}, actor)
# Create member WITHOUT fee type first to avoid auto-generation
member =
create_member_without_cycles(%{
join_date: ~D[2022-03-15],
membership_fee_start_date: ~D[2022-01-01]
})
create_member_without_cycles(
%{
join_date: ~D[2022-03-15],
membership_fee_start_date: ~D[2022-01-01]
},
actor
)
# Assign fee type
member =
member
|> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: fee_type.id})
|> Ash.update!()
|> Ash.update!(actor: actor)
# Explicitly generate cycles with fixed "today" date to avoid date dependency
today = ~D[2024-06-15]
{:ok, _, _} = CycleGenerator.generate_cycles_for_member(member.id, today: today)
# Verify cycles were generated
all_cycles = get_member_cycles(member.id)
all_cycles = get_member_cycles(member.id, actor)
cycle_years = Enum.map(all_cycles, & &1.cycle_start.year) |> Enum.sort() |> Enum.uniq()
# With include_joining_cycle=true and join_date=2022-03-15,
@ -92,16 +100,19 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
assert 2024 in cycle_years
end
test "generates cycles from last existing cycle" do
setup_settings(true)
fee_type = create_fee_type(%{interval: :yearly})
test "generates cycles from last existing cycle", %{actor: actor} do
setup_settings(true, actor)
fee_type = create_fee_type(%{interval: :yearly}, actor)
# Create member without fee type first to avoid auto-generation
member =
create_member_without_cycles(%{
join_date: ~D[2022-03-15],
membership_fee_start_date: ~D[2022-01-01]
})
create_member_without_cycles(
%{
join_date: ~D[2022-03-15],
membership_fee_start_date: ~D[2022-01-01]
},
actor
)
# Manually create a cycle for 2022
MembershipFeeCycle
@ -112,13 +123,13 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
amount: fee_type.amount,
status: :paid
})
|> Ash.create!()
|> Ash.create!(actor: actor)
# Now assign fee type to member
member =
member
|> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: fee_type.id})
|> Ash.update!()
|> Ash.update!(actor: actor)
# Generate cycles with specific "today" date
today = ~D[2024-06-15]
@ -130,17 +141,20 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
assert 2022 not in new_cycle_years
end
test "respects left_at boundary (stops generation)" do
setup_settings(true)
fee_type = create_fee_type(%{interval: :yearly})
test "respects left_at boundary (stops generation)", %{actor: actor} do
setup_settings(true, actor)
fee_type = create_fee_type(%{interval: :yearly}, actor)
member =
create_member_without_cycles(%{
join_date: ~D[2022-03-15],
exit_date: ~D[2023-06-15],
membership_fee_type_id: fee_type.id,
membership_fee_start_date: ~D[2022-01-01]
})
create_member_without_cycles(
%{
join_date: ~D[2022-03-15],
exit_date: ~D[2023-06-15],
membership_fee_type_id: fee_type.id,
membership_fee_start_date: ~D[2022-01-01]
},
actor
)
# Generate cycles with specific "today" date far in the future
today = ~D[2025-06-15]
@ -154,16 +168,19 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
assert 2025 not in cycle_years
end
test "skips existing cycles (idempotent)" do
setup_settings(true)
fee_type = create_fee_type(%{interval: :yearly})
test "skips existing cycles (idempotent)", %{actor: actor} do
setup_settings(true, actor)
fee_type = create_fee_type(%{interval: :yearly}, actor)
member =
create_member_without_cycles(%{
join_date: ~D[2023-03-15],
membership_fee_type_id: fee_type.id,
membership_fee_start_date: ~D[2023-01-01]
})
create_member_without_cycles(
%{
join_date: ~D[2023-03-15],
membership_fee_type_id: fee_type.id,
membership_fee_start_date: ~D[2023-01-01]
},
actor
)
today = ~D[2024-06-15]
@ -177,37 +194,43 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
assert second_cycles == []
end
test "does not fill gaps when cycles were deleted" do
setup_settings(true)
fee_type = create_fee_type(%{interval: :yearly})
test "does not fill gaps when cycles were deleted", %{actor: actor} do
setup_settings(true, actor)
fee_type = create_fee_type(%{interval: :yearly}, actor)
# Create member without fee type first to control which cycles exist
member =
create_member_without_cycles(%{
join_date: ~D[2020-03-15],
membership_fee_start_date: ~D[2020-01-01]
})
create_member_without_cycles(
%{
join_date: ~D[2020-03-15],
membership_fee_start_date: ~D[2020-01-01]
},
actor
)
# Manually create cycles for 2020, 2021, 2022, 2023
for year <- [2020, 2021, 2022, 2023] do
MembershipFeeCycle
|> Ash.Changeset.for_create(:create, %{
cycle_start: Date.new!(year, 1, 1),
member_id: member.id,
membership_fee_type_id: fee_type.id,
amount: fee_type.amount,
status: :unpaid
})
|> Ash.create!()
|> Ash.Changeset.for_create(
:create,
%{
cycle_start: Date.new!(year, 1, 1),
member_id: member.id,
membership_fee_type_id: fee_type.id,
amount: fee_type.amount,
status: :unpaid
}
)
|> Ash.create!(actor: actor)
end
# Delete the 2021 cycle (create a gap)
cycle_2021 =
MembershipFeeCycle
|> Ash.Query.filter(member_id == ^member.id and cycle_start == ^~D[2021-01-01])
|> Ash.read_one!()
|> Ash.read_one!(actor: actor)
Ash.destroy!(cycle_2021)
Ash.destroy!(cycle_2021, actor: actor)
# Now assign fee type to member (this triggers generation)
# Since cycles already exist (2020, 2022, 2023), the generator will
@ -215,10 +238,10 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
member =
member
|> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: fee_type.id})
|> Ash.update!()
|> Ash.update!(actor: actor)
# Verify gap was NOT filled and new cycles were generated from last existing
all_cycles = get_member_cycles(member.id)
all_cycles = get_member_cycles(member.id, actor)
all_cycle_years = Enum.map(all_cycles, & &1.cycle_start.year) |> Enum.sort()
# 2021 should NOT exist (gap was not filled)
@ -234,20 +257,23 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
assert 2025 in all_cycle_years
end
test "sets correct amount from membership fee type" do
setup_settings(true)
test "sets correct amount from membership fee type", %{actor: actor} do
setup_settings(true, actor)
amount = Decimal.new("75.50")
fee_type = create_fee_type(%{interval: :yearly, amount: amount})
fee_type = create_fee_type(%{interval: :yearly, amount: amount}, actor)
member =
create_member_without_cycles(%{
join_date: ~D[2024-03-15],
membership_fee_type_id: fee_type.id,
membership_fee_start_date: ~D[2024-01-01]
})
create_member_without_cycles(
%{
join_date: ~D[2024-03-15],
membership_fee_type_id: fee_type.id,
membership_fee_start_date: ~D[2024-01-01]
},
actor
)
# Verify cycles were generated with correct amount
all_cycles = get_member_cycles(member.id)
all_cycles = get_member_cycles(member.id, actor)
refute Enum.empty?(all_cycles), "Expected cycles to be generated"
# All cycles should have the correct amount
@ -256,21 +282,24 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
end)
end
test "handles NULL membership_fee_start_date by calculating from join_date" do
setup_settings(true)
fee_type = create_fee_type(%{interval: :quarterly})
test "handles NULL membership_fee_start_date by calculating from join_date", %{actor: actor} do
setup_settings(true, actor)
fee_type = create_fee_type(%{interval: :quarterly}, actor)
# Create member without membership_fee_start_date - it will be auto-calculated
# and cycles will be auto-generated
member =
create_member_without_cycles(%{
join_date: ~D[2024-02-15],
membership_fee_type_id: fee_type.id
# No membership_fee_start_date - should be calculated
})
create_member_without_cycles(
%{
join_date: ~D[2024-02-15],
membership_fee_type_id: fee_type.id
# No membership_fee_start_date - should be calculated
},
actor
)
# Verify cycles were auto-generated
all_cycles = get_member_cycles(member.id)
all_cycles = get_member_cycles(member.id, actor)
# With include_joining_cycle=true and join_date=2024-02-15 (quarterly),
# start_date should be 2024-01-01 (Q1 start)
@ -284,28 +313,34 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
assert first_cycle_start == ~D[2024-01-01]
end
test "returns error when member has no membership_fee_type" do
test "returns error when member has no membership_fee_type", %{actor: actor} do
# Create member without fee type - no auto-generation will occur
member =
create_member_without_cycles(%{
join_date: ~D[2024-03-15]
# No membership_fee_type_id
})
create_member_without_cycles(
%{
join_date: ~D[2024-03-15]
# No membership_fee_type_id
},
actor
)
{:error, reason} = CycleGenerator.generate_cycles_for_member(member.id)
assert reason == :no_membership_fee_type
end
test "returns error when member has no join_date" do
fee_type = create_fee_type(%{interval: :yearly})
test "returns error when member has no join_date", %{actor: actor} do
fee_type = create_fee_type(%{interval: :yearly}, actor)
# Create member without join_date - no auto-generation will occur
# (after_action hook checks for join_date)
member =
create_member_without_cycles(%{
membership_fee_type_id: fee_type.id
# No join_date
})
create_member_without_cycles(
%{
membership_fee_type_id: fee_type.id
# No join_date
},
actor
)
{:error, reason} = CycleGenerator.generate_cycles_for_member(member.id)
assert reason == :no_join_date
@ -357,24 +392,30 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
end
describe "generate_cycles_for_all_members/1" do
test "generates cycles for multiple members" do
setup_settings(true)
fee_type = create_fee_type(%{interval: :yearly})
test "generates cycles for multiple members", %{actor: actor} do
setup_settings(true, actor)
fee_type = create_fee_type(%{interval: :yearly}, actor)
# Create multiple members
_member1 =
create_member_without_cycles(%{
join_date: ~D[2024-01-15],
membership_fee_type_id: fee_type.id,
membership_fee_start_date: ~D[2024-01-01]
})
create_member_without_cycles(
%{
join_date: ~D[2024-01-15],
membership_fee_type_id: fee_type.id,
membership_fee_start_date: ~D[2024-01-01]
},
actor
)
_member2 =
create_member_without_cycles(%{
join_date: ~D[2024-02-15],
membership_fee_type_id: fee_type.id,
membership_fee_start_date: ~D[2024-01-01]
})
create_member_without_cycles(
%{
join_date: ~D[2024-02-15],
membership_fee_type_id: fee_type.id,
membership_fee_start_date: ~D[2024-01-01]
},
actor
)
today = ~D[2024-06-15]
{:ok, results} = CycleGenerator.generate_cycles_for_all_members(today: today)
@ -387,16 +428,19 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
end
describe "lock mechanism" do
test "prevents concurrent generation for same member" do
setup_settings(true)
fee_type = create_fee_type(%{interval: :yearly})
test "prevents concurrent generation for same member", %{actor: actor} do
setup_settings(true, actor)
fee_type = create_fee_type(%{interval: :yearly}, actor)
member =
create_member_without_cycles(%{
join_date: ~D[2022-03-15],
membership_fee_type_id: fee_type.id,
membership_fee_start_date: ~D[2022-01-01]
})
create_member_without_cycles(
%{
join_date: ~D[2022-03-15],
membership_fee_type_id: fee_type.id,
membership_fee_start_date: ~D[2022-01-01]
},
actor
)
today = ~D[2024-06-15]