test: make CycleGenerator tests more robust

- Replace weak assertions (>= 0, if length > 0) with concrete expectations
- Remove unnecessary Process.sleep calls (tests run synchronously)
- Add get_member_cycles helper for direct cycle verification
- Tests now validate actual generated cycles instead of relying on async behavior
This commit is contained in:
Moritz 2025-12-12 16:25:41 +01:00
parent cf8a1fa30d
commit 272a8a8afc

View file

@ -50,31 +50,43 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
|> Ash.update!()
end
# Helper to get cycles for a member
defp get_member_cycles(member_id) do
MembershipFeeCycle
|> Ash.Query.filter(member_id == ^member_id)
|> Ash.Query.sort(cycle_start: :asc)
|> Ash.read!()
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})
# Create member WITHOUT fee type first, then assign it
# This avoids the auto-generation on create and gives us control
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]
})
# Wait a moment for async task to complete or skip it
Process.sleep(100)
# Assign fee type, which will trigger auto-generation in test env
member =
member
|> Ash.Changeset.for_update(:update_member, %{membership_fee_type_id: fee_type.id})
|> Ash.update!()
# Generate cycles with specific "today" date
today = ~D[2024-06-15]
{:ok, cycles} = CycleGenerator.generate_cycles_for_member(member.id, today: today)
# Verify cycles were generated
all_cycles = get_member_cycles(member.id)
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,
# start_date should be 2022-01-01
# Should generate cycles for 2022, 2023, 2024
_cycle_years = Enum.map(cycles, & &1.cycle_start.year) |> Enum.sort()
# May already have some cycles from the async trigger, so check we have at least 3
assert length(cycles) >= 0
# Should have cycles for 2022, 2023, 2024
assert 2022 in cycle_years
assert 2023 in cycle_years
assert 2024 in cycle_years
end
test "generates cycles from last existing cycle" do
@ -127,8 +139,6 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
membership_fee_start_date: ~D[2022-01-01]
})
Process.sleep(100)
# Generate cycles with specific "today" date far in the future
today = ~D[2025-06-15]
{:ok, cycles} = CycleGenerator.generate_cycles_for_member(member.id, today: today)
@ -152,8 +162,6 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
membership_fee_start_date: ~D[2023-01-01]
})
Process.sleep(100)
today = ~D[2024-06-15]
# First generation
@ -178,13 +186,12 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
membership_fee_start_date: ~D[2024-01-01]
})
Process.sleep(100)
today = ~D[2024-06-15]
{:ok, cycles} = CycleGenerator.generate_cycles_for_member(member.id, today: today)
# Verify cycles were generated with correct amount
all_cycles = get_member_cycles(member.id)
refute Enum.empty?(all_cycles), "Expected cycles to be generated"
# All cycles should have the correct amount
Enum.each(cycles, fn cycle ->
Enum.each(all_cycles, fn cycle ->
assert Decimal.equal?(cycle.amount, amount)
end)
end
@ -193,7 +200,8 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
setup_settings(true)
fee_type = create_fee_type(%{interval: :quarterly})
# Create member without membership_fee_start_date
# 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],
@ -201,33 +209,29 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
# No membership_fee_start_date - should be calculated
})
Process.sleep(100)
today = ~D[2024-06-15]
{:ok, cycles} = CycleGenerator.generate_cycles_for_member(member.id, today: today)
# Verify cycles were auto-generated
all_cycles = get_member_cycles(member.id)
# With include_joining_cycle=true and join_date=2024-02-15 (quarterly),
# start_date should be 2024-01-01
# Should have Q1 and Q2 2024 cycles
unless Enum.empty?(cycles) do
cycle_starts = Enum.map(cycles, & &1.cycle_start) |> Enum.sort(Date)
first_cycle_start = List.first(cycle_starts)
# start_date should be 2024-01-01 (Q1 start)
# Should have Q1, Q2, Q3, Q4 2024 cycles (based on current date)
refute Enum.empty?(all_cycles), "Expected cycles to be generated"
# First cycle should start in Q1 2024
assert first_cycle_start.year == 2024
assert first_cycle_start.month in [1, 4]
end
cycle_starts = Enum.map(all_cycles, & &1.cycle_start) |> Enum.sort(Date)
first_cycle_start = List.first(cycle_starts)
# First cycle should start in Q1 2024 (2024-01-01)
assert first_cycle_start == ~D[2024-01-01]
end
test "returns error when member has no membership_fee_type" 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
})
Process.sleep(100)
{:error, reason} = CycleGenerator.generate_cycles_for_member(member.id)
assert reason == :no_membership_fee_type
end
@ -235,14 +239,14 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
test "returns error when member has no join_date" do
fee_type = create_fee_type(%{interval: :yearly})
# 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
})
Process.sleep(100)
{:error, reason} = CycleGenerator.generate_cycles_for_member(member.id)
assert reason == :no_join_date
end
@ -312,8 +316,6 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
membership_fee_start_date: ~D[2024-01-01]
})
Process.sleep(200)
today = ~D[2024-06-15]
{:ok, results} = CycleGenerator.generate_cycles_for_all_members(today: today)
@ -336,8 +338,6 @@ defmodule Mv.MembershipFees.CycleGeneratorTest do
membership_fee_start_date: ~D[2022-01-01]
})
Process.sleep(100)
today = ~D[2024-06-15]
# Run two concurrent generations