test: add comprehensive tests for membership fee UI components

Add tests for all membership fee UI components following TDD principles:
This commit is contained in:
Moritz 2025-12-16 11:13:09 +01:00
parent bc989422e2
commit 5789079ab0
Signed by: moritz
GPG key ID: 1020A035E5DD0824
9 changed files with 1695 additions and 0 deletions

View file

@ -0,0 +1,153 @@
defmodule MvWeb.MemberLive.Index.MembershipFeeStatusTest do
@moduledoc """
Tests for MembershipFeeStatus helper module.
"""
use Mv.DataCase, async: true
alias MvWeb.MemberLive.Index.MembershipFeeStatus
alias Mv.Membership.Member
alias Mv.MembershipFees.MembershipFeeType
alias Mv.MembershipFees.MembershipFeeCycle
require Ash.Query
# Helper to create a membership fee type
defp create_fee_type(attrs) do
default_attrs = %{
name: "Test Fee Type #{System.unique_integer([:positive])}",
amount: Decimal.new("50.00"),
interval: :yearly
}
attrs = Map.merge(default_attrs, attrs)
MembershipFeeType
|> Ash.Changeset.for_create(:create, attrs)
|> Ash.create!()
end
# Helper to create a member
defp create_member(attrs) do
default_attrs = %{
first_name: "Test",
last_name: "Member",
email: "test.member.#{System.unique_integer([:positive])}@example.com"
}
attrs = Map.merge(default_attrs, attrs)
Member
|> Ash.Changeset.for_create(:create_member, attrs)
|> Ash.create!()
end
# Helper to create a cycle
defp create_cycle(member, fee_type, attrs) do
default_attrs = %{
cycle_start: ~D[2023-01-01],
amount: Decimal.new("50.00"),
member_id: member.id,
membership_fee_type_id: fee_type.id,
status: :unpaid
}
attrs = Map.merge(default_attrs, attrs)
MembershipFeeCycle
|> Ash.Changeset.for_create(:create, attrs)
|> Ash.create!()
end
describe "load_cycles_for_members/2" do
test "efficiently loads cycles for members" do
fee_type = create_fee_type(%{interval: :yearly})
member1 = create_member(%{membership_fee_type_id: fee_type.id})
member2 = create_member(%{membership_fee_type_id: fee_type.id})
create_cycle(member1, fee_type, %{cycle_start: ~D[2023-01-01], status: :paid})
create_cycle(member2, fee_type, %{cycle_start: ~D[2023-01-01], status: :unpaid})
query =
Member
|> Ash.Query.filter(id in [^member1.id, ^member2.id])
|> MembershipFeeStatus.load_cycles_for_members()
members = Ash.read!(query)
assert length(members) == 2
# Verify cycles are loaded
member1_loaded = Enum.find(members, &(&1.id == member1.id))
member2_loaded = Enum.find(members, &(&1.id == member2.id))
assert member1_loaded.membership_fee_cycles != nil
assert member2_loaded.membership_fee_cycles != nil
end
end
describe "get_cycle_status_for_member/2" do
test "returns status of last completed cycle" do
fee_type = create_fee_type(%{interval: :yearly})
member = create_member(%{membership_fee_type_id: fee_type.id})
create_cycle(member, fee_type, %{cycle_start: ~D[2022-01-01], status: :paid})
create_cycle(member, fee_type, %{cycle_start: ~D[2023-01-01], status: :unpaid})
today = ~D[2024-06-15]
status = MembershipFeeStatus.get_cycle_status_for_member(member, today, false)
# Should return status of 2023 cycle (last completed)
assert status == :unpaid
end
test "returns status of current cycle when show_current is true" do
fee_type = create_fee_type(%{interval: :yearly})
member = create_member(%{membership_fee_type_id: fee_type.id})
create_cycle(member, fee_type, %{cycle_start: ~D[2023-01-01], status: :paid})
create_cycle(member, fee_type, %{cycle_start: ~D[2024-01-01], status: :suspended})
today = ~D[2024-06-15]
status = MembershipFeeStatus.get_cycle_status_for_member(member, today, true)
# Should return status of 2024 cycle (current)
assert status == :suspended
end
test "returns nil if no cycles exist" do
fee_type = create_fee_type(%{interval: :yearly})
member = create_member(%{membership_fee_type_id: fee_type.id})
today = Date.utc_today()
status = MembershipFeeStatus.get_cycle_status_for_member(member, today, false)
assert status == nil
end
end
describe "format_cycle_status_badge/1" do
test "returns badge component for paid status" do
result = MembershipFeeStatus.format_cycle_status_badge(:paid)
assert result =~ "text-success"
assert result =~ "hero-check-circle"
end
test "returns badge component for unpaid status" do
result = MembershipFeeStatus.format_cycle_status_badge(:unpaid)
assert result =~ "text-error"
assert result =~ "hero-x-circle"
end
test "returns badge component for suspended status" do
result = MembershipFeeStatus.format_cycle_status_badge(:suspended)
assert result =~ "text-base-content/60"
assert result =~ "hero-pause-circle"
end
test "handles nil status gracefully" do
result = MembershipFeeStatus.format_cycle_status_badge(nil)
assert result =~ "text-base-content/60"
end
end
end