01cb4255ef
New MCP server providing real-time global intelligence: financial markets (Yahoo Finance, CoinGecko), economic indicators (FRED, EIA, World Bank), conflict tracking (ACLED, UCDP, HDX), military flights (OpenSky), infrastructure monitoring (Cloudflare Radar, NGA cable health), maritime warnings (NGA), climate anomalies (Open-Meteo), news aggregation (RSS, GDELT), prediction markets (Polymarket), displacement data (UNHCR), aviation delays (FAA), cyber threats (Feodo, CISA KEV, SANS, URLhaus), country intelligence briefs (Ollama LLM), and HTML report generation (Jinja2 + Chart.js). Includes: SQLite TTL cache, per-source circuit breakers, async HTTP fetcher with retry/rate-limiting, Click CLI with 27 commands, 4 analysis modules (instability scoring, geo-convergence, signal aggregation, news clustering), 4 HTML report templates, and 28 unit tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
60 lines
1.8 KiB
Python
60 lines
1.8 KiB
Python
"""Tests for circuit breaker and infrastructure."""
|
|
|
|
import time
|
|
|
|
import pytest
|
|
|
|
from world_intel_mcp.circuit_breaker import CircuitBreaker
|
|
|
|
|
|
def test_circuit_starts_closed() -> None:
|
|
cb = CircuitBreaker(failure_threshold=3, cooldown_seconds=1.0)
|
|
assert cb.is_available("test-source")
|
|
|
|
|
|
def test_circuit_trips_after_threshold() -> None:
|
|
cb = CircuitBreaker(failure_threshold=2, cooldown_seconds=10.0)
|
|
cb.record_failure("src")
|
|
assert cb.is_available("src") # 1 failure, threshold is 2
|
|
cb.record_failure("src")
|
|
assert not cb.is_available("src") # tripped
|
|
|
|
|
|
def test_circuit_recovers_after_cooldown() -> None:
|
|
cb = CircuitBreaker(failure_threshold=1, cooldown_seconds=0.5)
|
|
cb.record_failure("src")
|
|
assert not cb.is_available("src")
|
|
time.sleep(0.6)
|
|
assert cb.is_available("src") # half-open, allows probe
|
|
|
|
|
|
def test_success_resets_failures() -> None:
|
|
cb = CircuitBreaker(failure_threshold=3, cooldown_seconds=10.0)
|
|
cb.record_failure("src")
|
|
cb.record_failure("src")
|
|
cb.record_success("src")
|
|
assert cb.is_available("src")
|
|
# Even after 2 more failures, need 3 consecutive
|
|
cb.record_failure("src")
|
|
cb.record_failure("src")
|
|
assert cb.is_available("src") # only 2 since reset
|
|
|
|
|
|
def test_status_output() -> None:
|
|
cb = CircuitBreaker(failure_threshold=2, cooldown_seconds=60.0)
|
|
cb.record_success("healthy")
|
|
cb.record_failure("unhealthy")
|
|
cb.record_failure("unhealthy")
|
|
|
|
status = cb.status()
|
|
assert status["healthy"]["status"] == "closed"
|
|
assert status["unhealthy"]["status"] == "open"
|
|
assert status["unhealthy"]["total_trips"] == 1
|
|
|
|
|
|
def test_independent_sources() -> None:
|
|
cb = CircuitBreaker(failure_threshold=1, cooldown_seconds=60.0)
|
|
cb.record_failure("source_a")
|
|
assert not cb.is_available("source_a")
|
|
assert cb.is_available("source_b") # independent
|