mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-06-10 08:13:58 +02:00
Add MKT opt-in on threat intercept, jittered market fetches, and Sentinel multi-scene dossier.
Operators enable Polymarket/Kalshi correlation from Global Threat Intercept with a consent dialog; polls use a jittered schedule separate from the slow tier. Right-click Sentinel imagery returns up to three signed scenes again. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
"""Prediction market fetch timing uses jitter to reduce poll fingerprinting."""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from services.fetchers import prediction_markets as pm
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def clear_market_cache():
|
||||
pm._market_cache.clear()
|
||||
yield
|
||||
pm._market_cache.clear()
|
||||
|
||||
|
||||
def test_pre_fetch_jitter_sleeps_when_configured(monkeypatch):
|
||||
monkeypatch.setattr(pm, "_PRE_FETCH_JITTER_S", 10.0)
|
||||
sleeps: list[float] = []
|
||||
monkeypatch.setattr(pm.time, "sleep", lambda s: sleeps.append(s))
|
||||
monkeypatch.setattr(pm.random, "uniform", lambda _a, _b: 4.5)
|
||||
|
||||
pm._apply_pre_fetch_jitter()
|
||||
|
||||
assert sleeps == [4.5]
|
||||
|
||||
|
||||
def test_fetch_raw_applies_provider_gap(monkeypatch):
|
||||
monkeypatch.setenv("PREDICTION_MARKETS_ENABLED", "true")
|
||||
monkeypatch.setattr(pm, "_apply_pre_fetch_jitter", lambda: None)
|
||||
gap_calls: list[int] = []
|
||||
|
||||
def _track_gap():
|
||||
gap_calls.append(1)
|
||||
|
||||
monkeypatch.setattr(pm, "_apply_provider_gap_jitter", _track_gap)
|
||||
monkeypatch.setattr(pm, "_fetch_polymarket_events", lambda: [])
|
||||
monkeypatch.setattr(pm, "_fetch_kalshi_events", lambda: [])
|
||||
monkeypatch.setattr(pm, "_merge_markets", lambda _p, _k: [])
|
||||
|
||||
pm.fetch_prediction_markets_raw()
|
||||
|
||||
assert gap_calls == [1]
|
||||
|
||||
|
||||
def test_pace_provider_adds_per_page_jitter(monkeypatch):
|
||||
monkeypatch.setattr(pm, "_POLYMARKET_PAGE_DELAY_JITTER_S", 1.0)
|
||||
monkeypatch.setattr(pm, "_provider_last_request_at", {"polymarket": pm.time.monotonic()})
|
||||
monkeypatch.setattr(pm.random, "uniform", lambda _a, _b: 0.5)
|
||||
sleeps: list[float] = []
|
||||
monkeypatch.setattr(pm.time, "sleep", lambda s: sleeps.append(s))
|
||||
|
||||
pm._pace_provider("polymarket", 0.02)
|
||||
|
||||
assert sleeps == [pytest.approx(0.52)]
|
||||
@@ -0,0 +1,24 @@
|
||||
"""UI opt-in for prediction markets (Global Threat Intercept)."""
|
||||
|
||||
from services import prediction_markets_settings as pm_settings
|
||||
from services.fetchers import prediction_markets
|
||||
|
||||
|
||||
def test_ui_opt_in_enables_fetch(monkeypatch, tmp_path):
|
||||
opt_file = tmp_path / "prediction_markets_opt_in.json"
|
||||
monkeypatch.setattr(pm_settings, "_OPT_IN_FILE", opt_file)
|
||||
monkeypatch.delenv("PREDICTION_MARKETS_ENABLED", raising=False)
|
||||
|
||||
assert pm_settings.prediction_markets_fetch_enabled() is False
|
||||
|
||||
pm_settings.set_prediction_markets_ui_opt_in(True)
|
||||
assert pm_settings.prediction_markets_fetch_enabled() is True
|
||||
assert prediction_markets.prediction_markets_fetch_enabled() is True
|
||||
|
||||
|
||||
def test_env_force_on_without_ui_file(monkeypatch, tmp_path):
|
||||
opt_file = tmp_path / "prediction_markets_opt_in.json"
|
||||
monkeypatch.setattr(pm_settings, "_OPT_IN_FILE", opt_file)
|
||||
monkeypatch.setenv("PREDICTION_MARKETS_ENABLED", "true")
|
||||
|
||||
assert pm_settings.prediction_markets_fetch_enabled() is True
|
||||
@@ -0,0 +1,74 @@
|
||||
"""Right-click dossier returns up to 3 signed Sentinel-2 scenes."""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from services import sentinel_search as ss
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def clear_sentinel_cache():
|
||||
ss._sentinel_cache.clear()
|
||||
yield
|
||||
ss._sentinel_cache.clear()
|
||||
|
||||
|
||||
def _stac_feature(scene_id: str, dt: str, cloud: float) -> dict:
|
||||
href = f"https://sentinel2euwest.blob.core.windows.net/sentinel2-l2a/{scene_id}.tif"
|
||||
return {
|
||||
"id": scene_id,
|
||||
"bbox": [0, 0, 1, 1],
|
||||
"properties": {
|
||||
"datetime": dt,
|
||||
"eo:cloud_cover": cloud,
|
||||
"platform": "Sentinel-2A",
|
||||
},
|
||||
"assets": {
|
||||
"rendered_preview": {"href": href},
|
||||
"thumbnail": {"href": href},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@patch("services.sentinel_search.requests.get")
|
||||
@patch("services.sentinel_search.requests.post")
|
||||
def test_search_returns_three_scenes(mock_post, mock_get):
|
||||
mock_post.return_value = MagicMock(
|
||||
ok=True,
|
||||
raise_for_status=MagicMock(),
|
||||
json=lambda: {
|
||||
"features": [
|
||||
_stac_feature("s1", "2026-05-28T10:00:00Z", 5.0),
|
||||
_stac_feature("s2", "2026-05-20T10:00:00Z", 12.0),
|
||||
_stac_feature("s3", "2026-05-10T10:00:00Z", 18.0),
|
||||
],
|
||||
},
|
||||
)
|
||||
mock_get.return_value = MagicMock(
|
||||
ok=True,
|
||||
raise_for_status=MagicMock(),
|
||||
json=lambda: {"token": "sig=test"},
|
||||
)
|
||||
|
||||
result = ss.search_sentinel2_scene(29.0, 51.0)
|
||||
|
||||
assert result["found"] is True
|
||||
assert result["scene_id"] == "s1"
|
||||
assert len(result["scenes"]) == 3
|
||||
assert result["scenes"][1]["scene_id"] == "s2"
|
||||
assert "sig=test" in (result["scenes"][0]["fullres_url"] or "")
|
||||
|
||||
|
||||
@patch("services.sentinel_search.requests.post")
|
||||
def test_search_esri_fallback_has_no_scenes(mock_post):
|
||||
mock_post.return_value = MagicMock(
|
||||
ok=True,
|
||||
raise_for_status=MagicMock(),
|
||||
json=lambda: {"features": []},
|
||||
)
|
||||
|
||||
result = ss.search_sentinel2_scene(29.0, 51.0)
|
||||
|
||||
assert result["fallback"] is True
|
||||
assert "scenes" not in result
|
||||
@@ -45,8 +45,10 @@ def test_fimi_falsy_value_does_not_call_upstream(monkeypatch):
|
||||
|
||||
def test_prediction_markets_disabled_by_default(monkeypatch):
|
||||
from services.fetchers import _store, prediction_markets
|
||||
from services import prediction_markets_settings as pm_settings
|
||||
|
||||
monkeypatch.delenv("PREDICTION_MARKETS_ENABLED", raising=False)
|
||||
monkeypatch.setattr(pm_settings, "get_prediction_markets_ui_opt_in", lambda: False)
|
||||
monkeypatch.setitem(_store.latest_data, "prediction_markets", [{"id": "old"}])
|
||||
monkeypatch.setattr(
|
||||
prediction_markets, "fetch_prediction_markets_raw", _explode
|
||||
|
||||
Reference in New Issue
Block a user