"""Tests for source modules — uses respx to mock HTTP calls."""
import json
from pathlib import Path
from unittest.mock import AsyncMock, patch
import httpx
import pytest
import respx
from world_intel_mcp.cache import Cache
from world_intel_mcp.circuit_breaker import CircuitBreaker
from world_intel_mcp.fetcher import Fetcher
@pytest.fixture
def cache(tmp_path: Path) -> Cache:
return Cache(db_path=tmp_path / "test_cache.db")
@pytest.fixture
def fetcher(cache: Cache) -> Fetcher:
breaker = CircuitBreaker()
return Fetcher(cache=cache, breaker=breaker, default_timeout=5.0)
# ---------------------------------------------------------------------------
# Markets
# ---------------------------------------------------------------------------
@respx.mock
@pytest.mark.asyncio
async def test_fetch_market_quotes(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.markets import fetch_market_quotes
# Mock Yahoo Finance v8 chart response for ^GSPC
chart_response = {
"chart": {
"result": [{
"meta": {
"symbol": "^GSPC",
"regularMarketPrice": 5123.45,
"regularMarketChangePercent": 0.42,
"currency": "USD",
}
}]
}
}
respx.get("https://query1.finance.yahoo.com/v8/finance/chart/%5EGSPC").mock(
return_value=httpx.Response(200, json=chart_response)
)
result = await fetch_market_quotes(fetcher, symbols=["^GSPC"])
assert "quotes" in result
assert len(result["quotes"]) == 1
assert result["quotes"][0]["symbol"] == "^GSPC"
assert result["quotes"][0]["price"] == 5123.45
assert result["source"] == "yahoo-finance"
@respx.mock
@pytest.mark.asyncio
async def test_fetch_crypto_quotes(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.markets import fetch_crypto_quotes
coins = [
{
"id": "bitcoin",
"symbol": "btc",
"name": "Bitcoin",
"current_price": 98000,
"market_cap": 1900000000000,
"price_change_percentage_24h": 2.5,
"sparkline_in_7d": {"price": [95000, 96000, 97000, 98000]},
}
]
respx.get("https://api.coingecko.com/api/v3/coins/markets").mock(
return_value=httpx.Response(200, json=coins)
)
result = await fetch_crypto_quotes(fetcher, limit=5)
assert "coins" in result
assert len(result["coins"]) == 1
assert result["coins"][0]["symbol"] == "btc"
assert result["source"] == "coingecko"
# ---------------------------------------------------------------------------
# Seismology
# ---------------------------------------------------------------------------
@respx.mock
@pytest.mark.asyncio
async def test_fetch_earthquakes(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.seismology import fetch_earthquakes
geojson = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": "us7000abc1",
"properties": {
"mag": 5.2,
"place": "100km SSW of Somewhere",
"time": 1708700000000,
"tsunami": 0,
"felt": 15,
"alert": "green",
"url": "https://earthquake.usgs.gov/earthquakes/eventpage/us7000abc1",
},
"geometry": {
"type": "Point",
"coordinates": [-120.5, 35.2, 10.0],
},
}
],
}
respx.get("https://earthquake.usgs.gov/fdsnws/event/1/query").mock(
return_value=httpx.Response(200, json=geojson)
)
result = await fetch_earthquakes(fetcher, min_magnitude=4.0, hours=24)
assert result["count"] == 1
eq = result["earthquakes"][0]
assert eq["magnitude"] == 5.2
assert eq["id"] == "us7000abc1"
assert eq["depth_km"] == 10.0
assert eq["latitude"] == 35.2
assert eq["longitude"] == -120.5
assert result["source"] == "usgs"
# ---------------------------------------------------------------------------
# Wildfire
# ---------------------------------------------------------------------------
@respx.mock
@pytest.mark.asyncio
async def test_fetch_wildfires_no_api_key(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.wildfire import fetch_wildfires
with patch.dict("os.environ", {}, clear=False):
# Remove the key if present
import os
os.environ.pop("NASA_FIRMS_API_KEY", None)
result = await fetch_wildfires(fetcher, api_key=None)
assert "error" in result
@respx.mock
@pytest.mark.asyncio
async def test_fetch_wildfires_single_region(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.wildfire import fetch_wildfires
csv_data = (
"latitude,longitude,bright_ti4,scan,track,acq_date,acq_time,satellite,confidence,version,bright_ti5,frp,daynight\n"
"34.5,-118.2,350.0,0.5,0.5,2024-02-23,1200,N,high,2.0,290.0,45.0,D\n"
"34.6,-118.3,360.0,0.5,0.5,2024-02-23,1200,N,high,2.0,295.0,55.0,D\n"
"34.5,-118.2,340.0,0.5,0.5,2024-02-23,1200,N,low,2.0,285.0,30.0,D\n"
)
respx.get(url__regex=r".*firms\.modaps\.eosdis\.nasa\.gov.*").mock(
return_value=httpx.Response(200, text=csv_data)
)
result = await fetch_wildfires(fetcher, region="north_america", api_key="testkey")
assert "fires_by_region" in result
na = result["fires_by_region"]["north_america"]
assert na["count"] == 2 # only 2 high-confidence
assert result["total_fires"] == 2
# ---------------------------------------------------------------------------
# Economic
# ---------------------------------------------------------------------------
@respx.mock
@pytest.mark.asyncio
async def test_fetch_fred_series_no_key(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.economic import fetch_fred_series
import os
os.environ.pop("FRED_API_KEY", None)
result = await fetch_fred_series(fetcher, series_id="UNRATE", api_key=None)
assert "error" in result
@respx.mock
@pytest.mark.asyncio
async def test_fetch_world_bank_indicators(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.economic import fetch_world_bank_indicators
wb_response = [
{"page": 1, "pages": 1, "per_page": 5, "total": 2},
[
{"indicator": {"id": "NY.GDP.MKTP.CD", "value": "GDP"}, "date": "2023", "value": 25000000000000},
{"indicator": {"id": "NY.GDP.MKTP.CD", "value": "GDP"}, "date": "2022", "value": 24000000000000},
],
]
respx.get(url__regex=r".*api\.worldbank\.org.*").mock(
return_value=httpx.Response(200, json=wb_response)
)
result = await fetch_world_bank_indicators(fetcher, country="USA", indicators=["NY.GDP.MKTP.CD"])
assert "indicators" in result
assert len(result["indicators"]) == 1
assert result["indicators"][0]["id"] == "NY.GDP.MKTP.CD"
assert result["source"] == "world-bank"
# ---------------------------------------------------------------------------
# Health (disease outbreaks)
# ---------------------------------------------------------------------------
@respx.mock
@pytest.mark.asyncio
async def test_fetch_disease_outbreaks(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.health import fetch_disease_outbreaks
rss_xml = """<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"><channel><title>WHO</title>
<item>
<title>Ebola outbreak in DRC - Update 5</title>
<link>https://who.int/ebola-update</link>
<pubDate>Mon, 10 Feb 2026 12:00:00 GMT</pubDate>
<description>Ebola virus disease outbreak continues in North Kivu.</description>
</item>
<item>
<title>Seasonal influenza update</title>
<link>https://who.int/flu</link>
<pubDate>Sun, 09 Feb 2026 08:00:00 GMT</pubDate>
<description>Northern hemisphere flu season report.</description>
</item>
</channel></rss>"""
# Mock all 3 health feeds returning same XML
respx.get(url__regex=r".*who\.int.*").mock(
return_value=httpx.Response(200, text=rss_xml)
)
respx.get(url__regex=r".*cdc\.gov.*").mock(
return_value=httpx.Response(200, text=rss_xml)
)
respx.get(url__regex=r".*outbreaknewstoday.*").mock(
return_value=httpx.Response(200, text=rss_xml)
)
result = await fetch_disease_outbreaks(fetcher)
assert result["source"] == "health-outbreak-monitor"
assert result["count"] > 0
assert result["high_concern_count"] > 0 # "ebola" in title
assert any(item["is_high_concern"] for item in result["items"])
# ---------------------------------------------------------------------------
# Sanctions (OFAC SDN)
# ---------------------------------------------------------------------------
@respx.mock
@pytest.mark.asyncio
async def test_fetch_sanctions_search(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.sanctions import fetch_sanctions_search
csv_data = (
'100,"DOE, John",individual,SDGT,"","","","","","","","nationality Iran; DOB 01 Jan 1970"\n'
'101,"ACME CORP",entity,CUBA,"","","","","","","",""\n'
'102,"SMITH, Jane",individual,SYRIA,"","","","","","","","nationality Syria"\n'
)
respx.get(url__regex=r".*treasury\.gov.*sdn\.csv.*").mock(
return_value=httpx.Response(200, text=csv_data)
)
result = await fetch_sanctions_search(fetcher, query="DOE")
assert result["source"] == "ofac-sdn"
assert result["count"] == 1
assert result["matches"][0]["name"] == "DOE, John"
assert result["total_entities"] >= 1
@respx.mock
@pytest.mark.asyncio
async def test_fetch_sanctions_search_country_filter(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.sanctions import fetch_sanctions_search
csv_data = (
'100,"DOE, John",individual,SDGT,"","","","","","","","nationality Iran; DOB 01 Jan 1970"\n'
'101,"ACME CORP",entity,CUBA,"","","","","","","",""\n'
)
respx.get(url__regex=r".*treasury\.gov.*sdn\.csv.*").mock(
return_value=httpx.Response(200, text=csv_data)
)
result = await fetch_sanctions_search(fetcher, country="iran")
assert result["count"] == 1
assert result["matches"][0]["name"] == "DOE, John"
# ---------------------------------------------------------------------------
# Elections (pure data — no HTTP mocking needed)
# ---------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_fetch_election_calendar(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.elections import fetch_election_calendar
result = await fetch_election_calendar(fetcher)
assert result["source"] == "election-calendar"
assert result["count"] > 0
assert "elections" in result
# Each election should have risk_score
for election in result["elections"]:
assert "risk_score" in election
assert "days_until" in election
assert election["status"] in ("past", "upcoming")
@pytest.mark.asyncio
async def test_fetch_election_calendar_country_filter(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.elections import fetch_election_calendar
result = await fetch_election_calendar(fetcher, country="USA")
# May or may not match depending on config data
assert result["source"] == "election-calendar"
assert isinstance(result["elections"], list)
# ---------------------------------------------------------------------------
# Shipping (Yahoo Finance quotes)
# ---------------------------------------------------------------------------
@respx.mock
@pytest.mark.asyncio
async def test_fetch_shipping_index(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.shipping import fetch_shipping_index
chart_response = {
"chart": {
"result": [{
"meta": {
"symbol": "BDRY",
"regularMarketPrice": 15.50,
"regularMarketChangePercent": 4.2,
"currency": "USD",
}
}]
}
}
# Mock all 4 shipping symbols
respx.get(url__regex=r".*finance\.yahoo\.com.*").mock(
return_value=httpx.Response(200, json=chart_response)
)
result = await fetch_shipping_index(fetcher)
assert result["source"] == "yahoo-finance"
assert len(result["quotes"]) > 0
assert isinstance(result["stress_score"], (int, float))
assert result["assessment"] in ("low", "moderate", "elevated", "high", "extreme")
# ---------------------------------------------------------------------------
# Social (Reddit)
# ---------------------------------------------------------------------------
@respx.mock
@pytest.mark.asyncio
async def test_fetch_social_signals(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.social import fetch_social_signals
reddit_response = {
"data": {
"children": [
{
"data": {
"title": "Ukraine conflict escalation analysis",
"score": 5000,
"num_comments": 300,
"upvote_ratio": 0.95,
"created_utc": 1708700000,
"permalink": "/r/worldnews/comments/abc123/",
"is_self": False,
}
},
{
"data": {
"title": "US-China trade tensions rise",
"score": 2000,
"num_comments": 150,
"upvote_ratio": 0.88,
"created_utc": 1708690000,
"permalink": "/r/worldnews/comments/def456/",
"is_self": True,
}
},
]
}
}
respx.get(url__regex=r".*reddit\.com.*hot\.json.*").mock(
return_value=httpx.Response(200, json=reddit_response)
)
result = await fetch_social_signals(fetcher)
assert result["source"] == "reddit-public"
assert result["velocity_metrics"]["total_posts"] > 0
assert result["velocity_metrics"]["high_engagement_count"] > 0
assert result["subreddits_queried"] == ["worldnews", "geopolitics"]
# ---------------------------------------------------------------------------
# Nuclear (USGS near test sites)
# ---------------------------------------------------------------------------
@respx.mock
@pytest.mark.asyncio
async def test_fetch_nuclear_monitor(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.nuclear import fetch_nuclear_monitor
geojson = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": "nn00900001",
"properties": {
"mag": 2.8,
"place": "50km N of Test Site",
"time": 1708700000000,
"tsunami": 0,
},
"geometry": {
"type": "Point",
"coordinates": [129.08, 41.30, 3.0], # Near Punggye-ri
},
}
],
}
# Mock USGS for all nuclear sites
respx.get("https://earthquake.usgs.gov/fdsnws/event/1/query").mock(
return_value=httpx.Response(200, json=geojson)
)
result = await fetch_nuclear_monitor(fetcher, hours=72)
assert result["source"] == "usgs-nuclear-monitor"
assert len(result["sites"]) == 5 # 5 nuclear test sites
assert isinstance(result["total_flagged_events"], int)
assert isinstance(result["critical_flags"], int)
# ---------------------------------------------------------------------------
# Infrastructure (Cloudflare + IODA fallback)
# ---------------------------------------------------------------------------
@respx.mock
@pytest.mark.asyncio
async def test_fetch_internet_outages_ioda_fallback(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.infrastructure import fetch_internet_outages
# IODA response shape
ioda_response = {
"data": [
{
"entity": {"code": "US", "name": "United States"},
"events": [
{
"id": "out-123",
"from": "2026-02-20T00:00:00Z",
"until": None,
"summary": "BGP outage detected",
"level": "country",
}
],
}
]
}
# Cloudflare returns 403 (no token)
respx.get(url__regex=r".*cloudflare\.com.*").mock(
return_value=httpx.Response(403, json={"error": "unauthorized"})
)
# IODA responds
respx.get(url__regex=r".*ioda\.inetintel.*").mock(
return_value=httpx.Response(200, json=ioda_response)
)
import os
os.environ.pop("CLOUDFLARE_API_TOKEN", None)
result = await fetch_internet_outages(fetcher)
assert result["source"] == "ioda-gatech"
assert result["total_7d"] == 1
assert result["ongoing_count"] == 1
@respx.mock
@pytest.mark.asyncio
async def test_fetch_cable_health(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.infrastructure import fetch_cable_health
warnings = [
{
"msgYear": 2026,
"msgNumber": 42,
"navArea": "XII",
"subregion": "31",
"status": "in force",
"issueDate": "2026-02-20",
"text": "SUBMARINE CABLE OPERATIONS 40-30.5N/030-15.2E VESSELS ADVISED",
}
]
respx.get(url__regex=r".*nga\.mil.*broadcast-warn.*").mock(
return_value=httpx.Response(200, json=warnings)
)
result = await fetch_cable_health(fetcher)
assert result["source"] == "nga-msi"
assert "corridors" in result
assert len(result["corridors"]) == 6
assert result["cable_related_warnings"] >= 1 # "cable" keyword in text
# ---------------------------------------------------------------------------
# Conflict (UCDP + ACLED)
# ---------------------------------------------------------------------------
@respx.mock
@pytest.mark.asyncio
async def test_fetch_ucdp_events(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.conflict import fetch_ucdp_events
from datetime import datetime, timezone
today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
ucdp_response = {
"TotalPages": 1,
"Result": [
{
"id": 12345,
"relid": "11-1",
"year": 2026,
"date_start": today,
"date_end": today,
"country": "Ukraine",
"region": "Europe",
"type_of_violence": 1,
"side_a": "Government of Ukraine",
"side_b": "DPR",
"best": 5,
"high": 10,
"low": 2,
"latitude": 48.0,
"longitude": 37.5,
"source_article": "Reuters",
"source_headline": "Fighting continues",
}
],
}
respx.get(url__regex=r".*ucdpapi\.pcr\.uu\.se.*").mock(
return_value=httpx.Response(200, json=ucdp_response)
)
result = await fetch_ucdp_events(fetcher, days=30)
assert result["source"] == "ucdp"
assert result["count"] == 1
assert result["events"][0]["country"] == "Ukraine"
assert result["total_fatalities_best"] == 5
# ---------------------------------------------------------------------------
# Hacker News
# ---------------------------------------------------------------------------
@respx.mock
@pytest.mark.asyncio
async def test_fetch_hacker_news(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.hacker_news import fetch_hacker_news
respx.get("https://hacker-news.firebaseio.com/v0/topstories.json").mock(
return_value=httpx.Response(200, json=[101, 102])
)
respx.get("https://hacker-news.firebaseio.com/v0/item/101.json").mock(
return_value=httpx.Response(200, json={
"id": 101, "title": "Show HN: AI Tool", "url": "https://example.com",
"score": 200, "by": "user1", "time": 1700000000, "descendants": 50,
})
)
respx.get("https://hacker-news.firebaseio.com/v0/item/102.json").mock(
return_value=httpx.Response(200, json={
"id": 102, "title": "Rust 2.0", "url": "https://example.com/rust",
"score": 150, "by": "user2", "time": 1700001000, "descendants": 30,
})
)
result = await fetch_hacker_news(fetcher, limit=2)
assert result["source"] == "hackernews"
assert result["count"] == 2
assert result["stories"][0]["score"] >= result["stories"][1]["score"]
# ---------------------------------------------------------------------------
# GitHub Trending
# ---------------------------------------------------------------------------
@respx.mock
@pytest.mark.asyncio
async def test_fetch_trending_repos(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.github_trending import fetch_trending_repos
respx.get(url__regex=r".*api\.github\.com/search/repositories.*").mock(
return_value=httpx.Response(200, json={
"total_count": 1,
"items": [{
"full_name": "user/cool-repo",
"description": "A cool tool",
"html_url": "https://github.com/user/cool-repo",
"stargazers_count": 500,
"forks_count": 20,
"language": "Python",
"created_at": "2026-02-20T00:00:00Z",
"topics": ["ai", "ml"],
}],
})
)
result = await fetch_trending_repos(fetcher, limit=5)
assert result["source"] == "github"
assert result["count"] == 1
assert result["repos"][0]["name"] == "user/cool-repo"
assert result["repos"][0]["stars"] == 500
# ---------------------------------------------------------------------------
# arXiv Papers
# ---------------------------------------------------------------------------
@respx.mock
@pytest.mark.asyncio
async def test_fetch_arxiv_papers(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.arxiv_papers import fetch_arxiv_papers
arxiv_xml = """<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<entry>
<id>http://arxiv.org/abs/2602.12345v1</id>
<title>Attention Is Still All You Need</title>
<summary>We show transformers continue to dominate.</summary>
<author><name>Jane Doe</name></author>
<author><name>John Smith</name></author>
<published>2026-02-20T00:00:00Z</published>
<link href="http://arxiv.org/abs/2602.12345v1" rel="alternate"/>
<link href="http://arxiv.org/pdf/2602.12345v1" title="pdf" rel="related"/>
<category term="cs.AI"/>
<category term="cs.LG"/>
</entry>
</feed>"""
respx.get(url__regex=r".*export\.arxiv\.org/api/query.*").mock(
return_value=httpx.Response(200, text=arxiv_xml)
)
result = await fetch_arxiv_papers(fetcher, limit=5)
assert result["source"] == "arxiv"
assert result["count"] == 1
assert "Attention" in result["papers"][0]["title"]
assert len(result["papers"][0]["authors"]) == 2
# ---------------------------------------------------------------------------
# USA Spending
# ---------------------------------------------------------------------------
@respx.mock
@pytest.mark.asyncio
async def test_fetch_usa_spending(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.usa_spending import fetch_usa_spending
respx.get(url__regex=r".*api\.usaspending\.gov.*").mock(
return_value=httpx.Response(200, json={
"results": [{
"agency_name": "Department of Defense",
"abbreviation": "DOD",
"current_total_budget_authority_amount": 850000000000,
"obligated_amount": 700000000000,
"outlay_amount": 650000000000,
"agency_id": 97,
}],
"page_metadata": {"total": 1},
})
)
result = await fetch_usa_spending(fetcher, limit=5)
assert result["source"] == "usaspending"
assert result["count"] == 1
assert result["agencies"][0]["name"] == "Department of Defense"
# ---------------------------------------------------------------------------
# Environmental Events (NASA EONET)
# ---------------------------------------------------------------------------
@respx.mock
@pytest.mark.asyncio
async def test_fetch_environmental_events(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.environmental import fetch_environmental_events
respx.get(url__regex=r".*eonet\.gsfc\.nasa\.gov.*").mock(
return_value=httpx.Response(200, json={
"events": [{
"id": "EONET_1234",
"title": "Wildfire in California",
"categories": [{"id": "wildfires", "title": "Wildfires"}],
"sources": [{"id": "InciWeb", "url": "https://inciweb.example.com"}],
"geometry": [{"date": "2026-02-20T00:00:00Z", "coordinates": [-119.5, 34.5]}],
}],
})
)
result = await fetch_environmental_events(fetcher, days=7)
assert result["source"] == "eonet"
assert result["count"] == 1
assert "Wildfire" in result["events"][0]["title"]
# ---------------------------------------------------------------------------
# Disaster Alerts (GDACS)
# ---------------------------------------------------------------------------
@respx.mock
@pytest.mark.asyncio
async def test_fetch_disaster_alerts(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.environmental import fetch_disaster_alerts
gdacs_geojson = {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {
"eventtype": "EQ",
"eventname": "M6.5 Earthquake",
"alertlevel": "orange",
"alertscore": 2.5,
"severity": {"value": 6.5, "unit": "M"},
"country": "Turkey",
"fromdate": "2026-02-20T12:00:00Z",
"todate": "2026-02-20T12:05:00Z",
"url": {"report": "https://gdacs.example.com/report"},
"population": {"value": 500000},
},
"geometry": {"type": "Point", "coordinates": [29.0, 38.5]},
}],
}
respx.get(url__regex=r".*gdacs\.org.*").mock(
return_value=httpx.Response(200, json=gdacs_geojson)
)
result = await fetch_disaster_alerts(fetcher)
assert result["source"] == "gdacs"
assert result["count"] == 1
assert result["alerts"][0]["event_type"] == "EQ"
assert result["alerts"][0]["alert_level"] == "orange"
# ---------------------------------------------------------------------------
# Geospatial — Extended datasets (static, no HTTP mock needed)
# ---------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_fetch_undersea_cables() -> None:
from world_intel_mcp.sources.geospatial import fetch_undersea_cables
result = await fetch_undersea_cables()
assert result["count"] > 0
assert result["source"] == "static-geospatial"
assert "total_length_km" in result
assert "total_capacity_tbps" in result
@pytest.mark.asyncio
async def test_fetch_ai_datacenters() -> None:
from world_intel_mcp.sources.geospatial import fetch_ai_datacenters
result = await fetch_ai_datacenters(country="USA")
assert result["count"] > 0
assert result["source"] == "static-geospatial"
assert "total_power_mw" in result
@pytest.mark.asyncio
async def test_fetch_spaceports() -> None:
from world_intel_mcp.sources.geospatial import fetch_spaceports
result = await fetch_spaceports()
assert result["count"] > 0
assert result["source"] == "static-geospatial"
@pytest.mark.asyncio
async def test_fetch_critical_minerals() -> None:
from world_intel_mcp.sources.geospatial import fetch_critical_minerals
result = await fetch_critical_minerals(mineral="lithium")
assert result["count"] > 0
assert all(d["mineral"] == "lithium" for d in result["deposits"])
@pytest.mark.asyncio
async def test_fetch_stock_exchanges() -> None:
from world_intel_mcp.sources.geospatial import fetch_stock_exchanges
result = await fetch_stock_exchanges(tier="mega")
assert result["count"] > 0
assert all(e["tier"] == "mega" for e in result["exchanges"])
assert "total_market_cap_usd_t" in result
# ---------------------------------------------------------------------------
# Country Stocks
# ---------------------------------------------------------------------------
@respx.mock
@pytest.mark.asyncio
async def test_fetch_country_stocks(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.markets import fetch_country_stocks
chart_response = {
"chart": {
"result": [{
"meta": {
"symbol": "^GSPC",
"regularMarketPrice": 5200.0,
"regularMarketChangePercent": 0.75,
"currency": "USD",
}
}]
}
}
respx.get(url__regex=r".*query1\.finance\.yahoo\.com.*").mock(
return_value=httpx.Response(200, json=chart_response)
)
result = await fetch_country_stocks(fetcher, country="USA")
assert result["source"] == "yahoo-finance"
assert result["country"] == "USA"
assert "quote" in result
# ---------------------------------------------------------------------------
# Aircraft Batch
# ---------------------------------------------------------------------------
@respx.mock
@pytest.mark.asyncio
async def test_fetch_aircraft_details_batch(fetcher: Fetcher) -> None:
from world_intel_mcp.sources.military import fetch_aircraft_details_batch
respx.get(url__regex=r".*hexdb\.io/api/v1/aircraft/ae1234.*").mock(
return_value=httpx.Response(200, json={
"Registration": "12-3456",
"Type": "C-17A",
"Operator": "USAF",
})
)
respx.get(url__regex=r".*hexdb\.io/api/v1/aircraft/ae5678.*").mock(
return_value=httpx.Response(200, json={
"Registration": "78-9012",
"Type": "KC-135R",
"Operator": "USAF",
})
)
result = await fetch_aircraft_details_batch(fetcher, icao24_list=["ae1234", "ae5678"])
assert result["source"] == "hexdb"
assert result["count"] == 2
assert result["requested"] == 2