Files
stonks-oracle/tests/test_scheduler.py
T

132 lines
4.7 KiB
Python

"""Tests for scheduler polling logic."""
from datetime import datetime, timedelta
from services.scheduler.app import (
DEFAULT_CADENCES,
MAX_RETRY_COUNT,
build_job_payload,
compute_backoff,
get_cadence_for_source,
is_source_due,
)
class TestGetCadenceForSource:
def test_default_cadence_market_api(self):
assert get_cadence_for_source("market_api", None) == 300
def test_default_cadence_news_api(self):
assert get_cadence_for_source("news_api", {}) == 300
def test_default_cadence_unknown_type(self):
assert get_cadence_for_source("unknown", None) == 600
def test_override_from_config(self):
config = {"polling_interval_seconds": 120}
assert get_cadence_for_source("market_api", config) == 120
def test_override_minimum_clamp(self):
config = {"polling_interval_seconds": 5}
assert get_cadence_for_source("market_api", config) == 10
def test_invalid_override_falls_back(self):
config = {"polling_interval_seconds": "not_a_number"}
assert get_cadence_for_source("news_api", config) == DEFAULT_CADENCES["news_api"]
class TestComputeBackoff:
def test_first_retry(self):
assert compute_backoff(0) == 60
def test_second_retry(self):
assert compute_backoff(1) == 120
def test_capped_at_max(self):
assert compute_backoff(20) == 3600
class TestIsSourceDue:
def _now(self):
return datetime(2026, 4, 11, 12, 0, 0)
def test_never_run_is_due(self):
assert is_source_due("market_api", None, None, None, 0, None, self._now())
def test_completed_within_cadence_not_due(self):
last = self._now() - timedelta(seconds=30)
assert not is_source_due("market_api", None, last, "completed", 0, None, self._now())
def test_completed_past_cadence_is_due(self):
last = self._now() - timedelta(seconds=400)
assert is_source_due("market_api", None, last, "completed", 0, None, self._now())
def test_running_not_due(self):
last = self._now() - timedelta(seconds=5)
assert not is_source_due("market_api", None, last, "running", 0, None, self._now())
def test_failed_within_backoff_not_due(self):
last = self._now() - timedelta(seconds=30)
next_retry = self._now() + timedelta(seconds=30)
assert not is_source_due("market_api", None, last, "failed", 1, next_retry, self._now())
def test_failed_past_backoff_is_due(self):
last = self._now() - timedelta(seconds=120)
next_retry = self._now() - timedelta(seconds=10)
assert is_source_due("market_api", None, last, "failed", 1, next_retry, self._now())
def test_failed_max_retries_not_due(self):
last = self._now() - timedelta(seconds=120)
assert not is_source_due(
"market_api", None, last, "failed", MAX_RETRY_COUNT, None, self._now()
)
def test_custom_cadence_respected(self):
config = {"polling_interval_seconds": 600}
last = self._now() - timedelta(seconds=300)
assert not is_source_due("market_api", config, last, "completed", 0, None, self._now())
last_old = self._now() - timedelta(seconds=700)
assert is_source_due("market_api", config, last_old, "completed", 0, None, self._now())
class TestBuildJobPayload:
def test_payload_structure(self):
source = {
"source_id": "sid-1",
"company_id": "cid-1",
"ticker": "AAPL",
"legal_name": "Apple Inc.",
"source_type": "news_api",
"source_name": "NewsAPI",
"config": {"endpoint": "/v2/everything"},
"credibility_score": 0.8,
}
now = datetime(2026, 4, 11, 12, 0, 0)
job = build_job_payload(source, ["Apple", "iPhone"], now)
assert job["source_id"] == "sid-1"
assert job["company_id"] == "cid-1"
assert job["ticker"] == "AAPL"
assert job["legal_name"] == "Apple Inc."
assert job["aliases"] == ["Apple", "iPhone"]
assert job["source_type"] == "news_api"
assert job["config"] == {"endpoint": "/v2/everything"}
assert job["credibility_score"] == 0.8
assert job["scheduled_at"] == now.isoformat()
def test_payload_null_config(self):
source = {
"source_id": "sid-2",
"company_id": "cid-2",
"ticker": "MSFT",
"legal_name": "Microsoft Corp.",
"source_type": "market_api",
"source_name": "Polygon",
"config": None,
"credibility_score": None,
}
job = build_job_payload(source, [], datetime(2026, 4, 11, 12, 0, 0))
assert job["config"] == {}
assert job["credibility_score"] == 0.5
assert job["aliases"] == []