"""Tests for the life-events → cashflow-array helper.""" from __future__ import annotations import numpy as np import pytest from fire_planner.life_events import EventInput, events_to_cashflow_array def test_empty_events_yield_zero_array() -> None: arr = events_to_cashflow_array([], horizon_years=5) np.testing.assert_array_equal(arr, np.zeros(5)) def test_one_time_event_lands_at_year_start() -> None: arr = events_to_cashflow_array( [EventInput(year_start=3, one_time_amount_gbp=250_000)], horizon_years=10, ) expected = np.zeros(10) expected[3] = 250_000 np.testing.assert_array_equal(arr, expected) def test_ranged_delta_applied_inclusive() -> None: arr = events_to_cashflow_array( [EventInput(year_start=2, year_end=5, delta_gbp_per_year=-10_000)], horizon_years=10, ) expected = np.zeros(10) expected[2:6] = -10_000 # 2,3,4,5 inclusive np.testing.assert_array_equal(arr, expected) def test_year_end_none_is_one_time() -> None: """Ranged events default year_end == year_start.""" arr = events_to_cashflow_array( [EventInput(year_start=4, year_end=None, delta_gbp_per_year=-5_000)], horizon_years=10, ) expected = np.zeros(10) expected[4] = -5_000 np.testing.assert_array_equal(arr, expected) def test_disabled_events_skipped() -> None: arr = events_to_cashflow_array( [ EventInput(year_start=0, one_time_amount_gbp=1_000_000, enabled=False), EventInput(year_start=1, delta_gbp_per_year=-50_000, year_end=3, enabled=False), ], horizon_years=5, ) np.testing.assert_array_equal(arr, np.zeros(5)) def test_events_past_horizon_clipped() -> None: """Events starting at or beyond the horizon don't apply at all; ranged events that overlap the horizon get clipped to the last year.""" arr = events_to_cashflow_array( [ EventInput(year_start=10, one_time_amount_gbp=100_000), EventInput(year_start=8, year_end=15, delta_gbp_per_year=-5_000), ], horizon_years=10, ) # First event: year 10 is outside (horizon 0..9), so nothing. # Second event: clipped to years 8..9. expected = np.zeros(10) expected[8] = -5_000 expected[9] = -5_000 np.testing.assert_array_equal(arr, expected) def test_multiple_events_sum() -> None: arr = events_to_cashflow_array( [ EventInput(year_start=0, year_end=4, delta_gbp_per_year=-12_000), EventInput(year_start=2, one_time_amount_gbp=50_000), EventInput(year_start=3, delta_gbp_per_year=20_000, year_end=10), ], horizon_years=10, ) expected = np.zeros(10) expected[0:5] += -12_000 # event 1: years 0..4 expected[2] += 50_000 # event 2: year 2 lump sum expected[3:10] += 20_000 # event 3: years 3..9 (clipped from 3..10) np.testing.assert_array_equal(arr, expected) def test_negative_year_start_clipped_to_zero() -> None: arr = events_to_cashflow_array( [EventInput(year_start=-2, year_end=2, delta_gbp_per_year=-1_000)], horizon_years=5, ) expected = np.zeros(5) expected[0:3] = -1_000 # 0,1,2 np.testing.assert_array_equal(arr, expected) @pytest.mark.parametrize("amount", [0, 0.0, None]) def test_zero_or_none_one_time_amount_skipped(amount: float | None) -> None: arr = events_to_cashflow_array( [EventInput(year_start=2, one_time_amount_gbp=amount)], horizon_years=5, ) np.testing.assert_array_equal(arr, np.zeros(5))