181 lines
7.3 KiB
Elixir
181 lines
7.3 KiB
Elixir
defmodule Mv.MembershipFees.CalendarCyclesTest do
|
|
@moduledoc """
|
|
Tests for CalendarCycles module.
|
|
"""
|
|
use ExUnit.Case, async: true
|
|
|
|
alias Mv.MembershipFees.CalendarCycles
|
|
|
|
doctest Mv.MembershipFees.CalendarCycles
|
|
|
|
describe "calculate_cycle_start/3" do
|
|
test "uses reference_date when provided" do
|
|
date = ~D[2024-03-15]
|
|
reference = ~D[2024-05-20]
|
|
|
|
assert CalendarCycles.calculate_cycle_start(date, :monthly, reference) == ~D[2024-05-01]
|
|
assert CalendarCycles.calculate_cycle_start(date, :quarterly, reference) == ~D[2024-04-01]
|
|
end
|
|
end
|
|
|
|
describe "current_cycle?/3" do
|
|
# Basic examples are covered by doctests
|
|
|
|
test "works for all interval types" do
|
|
today = ~D[2024-03-15]
|
|
|
|
for interval <- [:monthly, :quarterly, :half_yearly, :yearly] do
|
|
cycle_start = CalendarCycles.calculate_cycle_start(today, interval)
|
|
result = CalendarCycles.current_cycle?(cycle_start, interval, today)
|
|
|
|
assert result == true, "Expected current cycle for #{interval} with start #{cycle_start}"
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "current_cycle?/2 wrapper" do
|
|
test "calls current_cycle?/3 with Date.utc_today()" do
|
|
today = Date.utc_today()
|
|
cycle_start = CalendarCycles.calculate_cycle_start(today, :monthly)
|
|
|
|
# This test verifies the wrapper works, but uses actual today
|
|
# The real testing happens in current_cycle?/3 tests above
|
|
result = CalendarCycles.current_cycle?(cycle_start, :monthly)
|
|
|
|
assert result == true
|
|
end
|
|
end
|
|
|
|
describe "last_completed_cycle?/3" do
|
|
# Basic examples are covered by doctests
|
|
|
|
test "returns false when next cycle has also ended" do
|
|
# Two cycles ago: cycle ended, but next cycle also ended
|
|
today = ~D[2024-05-15]
|
|
cycle_start = ~D[2024-03-01]
|
|
# Cycle ended 2024-03-31, next cycle ended 2024-04-30, today is 2024-05-15
|
|
|
|
assert CalendarCycles.last_completed_cycle?(cycle_start, :monthly, today) == false
|
|
end
|
|
|
|
test "works correctly for quarterly intervals" do
|
|
# Q1 2024 ended on 2024-03-31
|
|
# Q2 2024 ends on 2024-06-30
|
|
# Today is 2024-04-15 (after Q1 ended, before Q2 ended)
|
|
today = ~D[2024-04-15]
|
|
past_quarter_start = ~D[2024-01-01]
|
|
|
|
assert CalendarCycles.last_completed_cycle?(past_quarter_start, :quarterly, today) == true
|
|
end
|
|
|
|
test "returns false when cycle ended on the given date" do
|
|
# Cycle ends on today, so it's still current, not completed
|
|
today = ~D[2024-03-31]
|
|
cycle_start = ~D[2024-03-01]
|
|
|
|
assert CalendarCycles.last_completed_cycle?(cycle_start, :monthly, today) == false
|
|
end
|
|
end
|
|
|
|
describe "last_completed_cycle?/2 wrapper" do
|
|
test "calls last_completed_cycle?/3 with Date.utc_today()" do
|
|
today = Date.utc_today()
|
|
cycle_start = CalendarCycles.calculate_cycle_start(today, :monthly)
|
|
|
|
# This test verifies the wrapper works, but uses actual today
|
|
# The real testing happens in last_completed_cycle?/3 tests above
|
|
result = CalendarCycles.last_completed_cycle?(cycle_start, :monthly)
|
|
|
|
# Result depends on actual today, so we just verify it's a boolean
|
|
assert is_boolean(result)
|
|
end
|
|
end
|
|
|
|
describe "edge cases" do
|
|
test "leap year: February has 29 days" do
|
|
# 2024 is a leap year
|
|
assert CalendarCycles.calculate_cycle_end(~D[2024-02-01], :monthly) == ~D[2024-02-29]
|
|
|
|
# 2023 is not a leap year
|
|
assert CalendarCycles.calculate_cycle_end(~D[2023-02-01], :monthly) == ~D[2023-02-28]
|
|
end
|
|
|
|
test "year boundary: December 31 to January 1" do
|
|
# Yearly cycle
|
|
assert CalendarCycles.next_cycle_start(~D[2024-01-01], :yearly) == ~D[2025-01-01]
|
|
|
|
# Monthly cycle across year boundary
|
|
assert CalendarCycles.next_cycle_start(~D[2024-12-01], :monthly) == ~D[2025-01-01]
|
|
|
|
# Half-yearly cycle across year boundary
|
|
assert CalendarCycles.next_cycle_start(~D[2024-07-01], :half_yearly) == ~D[2025-01-01]
|
|
|
|
# Quarterly cycle across year boundary
|
|
assert CalendarCycles.next_cycle_start(~D[2024-10-01], :quarterly) == ~D[2025-01-01]
|
|
end
|
|
|
|
test "month boundary: different month lengths" do
|
|
# 31-day months
|
|
assert CalendarCycles.calculate_cycle_end(~D[2024-01-01], :monthly) == ~D[2024-01-31]
|
|
assert CalendarCycles.calculate_cycle_end(~D[2024-03-01], :monthly) == ~D[2024-03-31]
|
|
assert CalendarCycles.calculate_cycle_end(~D[2024-05-01], :monthly) == ~D[2024-05-31]
|
|
|
|
# 30-day months
|
|
assert CalendarCycles.calculate_cycle_end(~D[2024-04-01], :monthly) == ~D[2024-04-30]
|
|
assert CalendarCycles.calculate_cycle_end(~D[2024-06-01], :monthly) == ~D[2024-06-30]
|
|
assert CalendarCycles.calculate_cycle_end(~D[2024-09-01], :monthly) == ~D[2024-09-30]
|
|
assert CalendarCycles.calculate_cycle_end(~D[2024-11-01], :monthly) == ~D[2024-11-30]
|
|
end
|
|
|
|
test "date in middle of cycle: all functions work correctly" do
|
|
middle_date = ~D[2024-03-15]
|
|
|
|
# calculate_cycle_start
|
|
assert CalendarCycles.calculate_cycle_start(middle_date, :monthly) == ~D[2024-03-01]
|
|
assert CalendarCycles.calculate_cycle_start(middle_date, :quarterly) == ~D[2024-01-01]
|
|
assert CalendarCycles.calculate_cycle_start(middle_date, :half_yearly) == ~D[2024-01-01]
|
|
assert CalendarCycles.calculate_cycle_start(middle_date, :yearly) == ~D[2024-01-01]
|
|
|
|
# calculate_cycle_end
|
|
monthly_start = CalendarCycles.calculate_cycle_start(middle_date, :monthly)
|
|
assert CalendarCycles.calculate_cycle_end(monthly_start, :monthly) == ~D[2024-03-31]
|
|
|
|
# next_cycle_start
|
|
assert CalendarCycles.next_cycle_start(monthly_start, :monthly) == ~D[2024-04-01]
|
|
end
|
|
|
|
test "quarterly: all quarter boundaries correct" do
|
|
# Q1 boundaries
|
|
assert CalendarCycles.calculate_cycle_start(~D[2024-01-15], :quarterly) == ~D[2024-01-01]
|
|
assert CalendarCycles.calculate_cycle_end(~D[2024-01-01], :quarterly) == ~D[2024-03-31]
|
|
|
|
# Q2 boundaries
|
|
assert CalendarCycles.calculate_cycle_start(~D[2024-05-15], :quarterly) == ~D[2024-04-01]
|
|
assert CalendarCycles.calculate_cycle_end(~D[2024-04-01], :quarterly) == ~D[2024-06-30]
|
|
|
|
# Q3 boundaries
|
|
assert CalendarCycles.calculate_cycle_start(~D[2024-08-15], :quarterly) == ~D[2024-07-01]
|
|
assert CalendarCycles.calculate_cycle_end(~D[2024-07-01], :quarterly) == ~D[2024-09-30]
|
|
|
|
# Q4 boundaries
|
|
assert CalendarCycles.calculate_cycle_start(~D[2024-11-15], :quarterly) == ~D[2024-10-01]
|
|
assert CalendarCycles.calculate_cycle_end(~D[2024-10-01], :quarterly) == ~D[2024-12-31]
|
|
end
|
|
|
|
test "half_yearly: both half boundaries correct" do
|
|
# First half boundaries
|
|
assert CalendarCycles.calculate_cycle_start(~D[2024-03-15], :half_yearly) == ~D[2024-01-01]
|
|
assert CalendarCycles.calculate_cycle_end(~D[2024-01-01], :half_yearly) == ~D[2024-06-30]
|
|
|
|
# Second half boundaries
|
|
assert CalendarCycles.calculate_cycle_start(~D[2024-09-15], :half_yearly) == ~D[2024-07-01]
|
|
assert CalendarCycles.calculate_cycle_end(~D[2024-07-01], :half_yearly) == ~D[2024-12-31]
|
|
end
|
|
|
|
test "yearly: full year boundaries" do
|
|
assert CalendarCycles.calculate_cycle_start(~D[2024-06-15], :yearly) == ~D[2024-01-01]
|
|
assert CalendarCycles.calculate_cycle_end(~D[2024-01-01], :yearly) == ~D[2024-12-31]
|
|
assert CalendarCycles.next_cycle_start(~D[2024-01-01], :yearly) == ~D[2025-01-01]
|
|
end
|
|
end
|
|
end
|