You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
111 lines
3.5 KiB
Python
111 lines
3.5 KiB
Python
"""Unit tests for financial feature calculations."""
|
|
import pytest
|
|
|
|
from libs.oracle_client.models import FinancialDataResponse, FinancialPeriod
|
|
|
|
TWO_PERIOD_RESPONSE = FinancialDataResponse(
|
|
ticker="AAPL",
|
|
periods=[
|
|
FinancialPeriod(
|
|
period="2026-Q1",
|
|
period_end="2025-12-28",
|
|
revenue=124_300_000_000,
|
|
net_income=36_000_000_000,
|
|
eps=2.34,
|
|
gross_margin=0.472,
|
|
operating_margin=0.315,
|
|
),
|
|
FinancialPeriod(
|
|
period="2025-Q4",
|
|
period_end="2025-09-27",
|
|
revenue=119_600_000_000,
|
|
net_income=34_900_000_000,
|
|
eps=2.26,
|
|
gross_margin=0.461,
|
|
operating_margin=0.308,
|
|
),
|
|
],
|
|
)
|
|
|
|
|
|
def test_compute_financial_features_latest_values():
|
|
from libs.features.financial_features import compute_financial_features
|
|
|
|
features = compute_financial_features(TWO_PERIOD_RESPONSE)
|
|
|
|
assert features["latest_eps"] == pytest.approx(2.34)
|
|
assert features["latest_gross_margin"] == pytest.approx(0.472)
|
|
assert features["latest_operating_margin"] == pytest.approx(0.315)
|
|
|
|
|
|
def test_eps_growth_qoq():
|
|
from libs.features.financial_features import compute_financial_features
|
|
|
|
features = compute_financial_features(TWO_PERIOD_RESPONSE)
|
|
|
|
expected = (2.34 - 2.26) / abs(2.26)
|
|
assert features["eps_growth_qoq"] == pytest.approx(expected)
|
|
|
|
|
|
def test_revenue_growth_qoq():
|
|
from libs.features.financial_features import compute_financial_features
|
|
|
|
features = compute_financial_features(TWO_PERIOD_RESPONSE)
|
|
|
|
expected = (124_300_000_000 - 119_600_000_000) / 119_600_000_000
|
|
assert features["revenue_growth_qoq"] == pytest.approx(expected)
|
|
|
|
|
|
def test_periods_sorted_by_period_end_descending():
|
|
from libs.features.financial_features import compute_financial_features
|
|
|
|
# Provide periods out of chronological order; latest should still be picked
|
|
response = FinancialDataResponse(
|
|
ticker="AAPL",
|
|
periods=[
|
|
FinancialPeriod(period="2025-Q4", period_end="2025-09-27", eps=2.26),
|
|
FinancialPeriod(period="2026-Q1", period_end="2025-12-28", eps=2.34),
|
|
],
|
|
)
|
|
features = compute_financial_features(response)
|
|
assert features["latest_eps"] == pytest.approx(2.34)
|
|
|
|
|
|
def test_single_period_no_growth_fields():
|
|
from libs.features.financial_features import compute_financial_features
|
|
|
|
response = FinancialDataResponse(
|
|
ticker="AAPL",
|
|
periods=[
|
|
FinancialPeriod(
|
|
period="2026-Q1", period_end="2025-12-28", eps=2.34, gross_margin=0.472
|
|
)
|
|
],
|
|
)
|
|
features = compute_financial_features(response)
|
|
|
|
assert features["latest_eps"] == pytest.approx(2.34)
|
|
assert features["eps_growth_qoq"] is None
|
|
assert features["revenue_growth_qoq"] is None
|
|
|
|
|
|
def test_empty_periods_returns_empty_dict():
|
|
from libs.features.financial_features import compute_financial_features
|
|
|
|
response = FinancialDataResponse(ticker="AAPL", periods=[])
|
|
assert compute_financial_features(response) == {}
|
|
|
|
|
|
def test_none_eps_in_prior_skips_growth():
|
|
from libs.features.financial_features import compute_financial_features
|
|
|
|
response = FinancialDataResponse(
|
|
ticker="AAPL",
|
|
periods=[
|
|
FinancialPeriod(period="2026-Q1", period_end="2025-12-28", eps=2.34),
|
|
FinancialPeriod(period="2025-Q4", period_end="2025-09-27", eps=None),
|
|
],
|
|
)
|
|
features = compute_financial_features(response)
|
|
assert features["eps_growth_qoq"] is None
|