bmc_hub/tests/test_subscription_billing_calendar.py
2026-08-28 20:49:55 +02:00

76 lines
2.7 KiB
Python

from datetime import date
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from app.services.subscription_billing_calendar import (
advance_billing_periods,
billing_date_for_period,
is_danish_bank_day,
next_billing_date,
prorated_30_day_factor,
resolve_month_date,
validate_billing_schedule,
)
import pytest
from app.services.subscription_agreement import agreement_status
def test_fixed_day_is_resolved_in_target_month():
assert next_billing_date(date(2026, 1, 17), "monthly", "fixed_day", 5) == date(2026, 2, 5)
def test_first_bank_day_skips_weekend_and_new_year():
assert resolve_month_date(2026, 1, "first_business_day", None) == date(2026, 1, 2)
def test_last_bank_day_skips_new_years_eve():
assert resolve_month_date(2026, 12, "last_business_day", None) == date(2026, 12, 30)
def test_bank_holiday_after_ascension_is_closed():
assert not is_danish_bank_day(date(2026, 5, 15))
def test_biweekly_keeps_exact_interval_even_with_month_schedule():
assert next_billing_date(date(2026, 4, 1), "biweekly", "first_business_day", 1) == date(2026, 4, 15)
def test_short_intervals_are_forced_to_runnable_anchor_schedule():
assert validate_billing_schedule("daily", "fixed_day", 17) == ("interval_anchor", 17)
def test_monthly_interval_anchor_is_rejected():
with pytest.raises(ValueError, match="only valid"):
validate_billing_schedule("monthly", "interval_anchor", 1)
def test_new_fixed_day_rules_cannot_use_day_29_to_31():
with pytest.raises(ValueError, match="between 1 and 28"):
validate_billing_schedule("quarterly", "fixed_day", 31)
def test_period_can_be_invoiced_two_months_in_advance():
assert billing_date_for_period(date(2027, 1, 1), 2, "fixed_day", 1) == date(2026, 11, 1)
def test_three_monthly_periods_cover_a_quarter():
assert advance_billing_periods(date(2027, 1, 1), "monthly", 3) == date(2027, 4, 1)
def test_short_opening_period_uses_30_day_basis():
assert prorated_30_day_factor(date(2027, 1, 5), date(2027, 2, 1)) == 26 / 30
def test_agreement_status_prioritizes_failures_and_pending_changes():
subscriptions = [{"status": "active"}, {"status": "paused"}]
assert agreement_status(subscriptions, [{"status": "failed"}]) == "Kræver handling"
assert agreement_status(subscriptions, [{"status": "pending"}]) == "Ændring afventer"
assert agreement_status(subscriptions, []) == "Delvist pauseret"
def test_agreement_status_handles_partial_termination_and_closed():
assert agreement_status([{"status": "active"}, {"status": "cancelled"}], []) == "Delvist opsagt"
assert agreement_status([{"status": "expired"}, {"status": "cancelled"}], []) == "Afsluttet"