diff --git a/test/mv/membership_fees/cycle_generator_test.exs b/test/mv/membership_fees/cycle_generator_test.exs index 64eacdc..1c2c35e 100644 --- a/test/mv/membership_fees/cycle_generator_test.exs +++ b/test/mv/membership_fees/cycle_generator_test.exs @@ -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