sync Cloudflare rules and cache scan snapshots
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
from scripts.configure_cloudflare_free import (
|
||||
MANAGED_RULE_REF_PREFIX,
|
||||
build_managed_cache_rules,
|
||||
merge_managed_rules,
|
||||
)
|
||||
|
||||
|
||||
def test_cloudflare_managed_rules_cache_public_content_then_bypass_sensitive_requests():
|
||||
rules = build_managed_cache_rules()
|
||||
|
||||
assert [rule["ref"] for rule in rules] == [
|
||||
f"{MANAGED_RULE_REF_PREFIX}public",
|
||||
f"{MANAGED_RULE_REF_PREFIX}bypass",
|
||||
]
|
||||
assert rules[0]["action_parameters"]["cache"] is True
|
||||
assert rules[-1]["action_parameters"]["cache"] is False
|
||||
assert 'http.host eq "api.polyweather.top"' in rules[-1]["expression"]
|
||||
assert 'http.request.uri.query contains "force_refresh=true"' in rules[-1]["expression"]
|
||||
|
||||
|
||||
def test_cloudflare_rule_merge_preserves_unmanaged_rules_and_puts_bypass_last():
|
||||
existing = [
|
||||
{
|
||||
"ref": "existing_rule",
|
||||
"description": "keep me",
|
||||
"expression": 'http.host eq "example.com"',
|
||||
"action": "set_cache_settings",
|
||||
"action_parameters": {"cache": True},
|
||||
"enabled": True,
|
||||
},
|
||||
{
|
||||
"ref": f"{MANAGED_RULE_REF_PREFIX}old",
|
||||
"description": "replace me",
|
||||
"expression": "true",
|
||||
"action": "set_cache_settings",
|
||||
"action_parameters": {"cache": False},
|
||||
"enabled": True,
|
||||
},
|
||||
]
|
||||
|
||||
merged = merge_managed_rules(existing, build_managed_cache_rules())
|
||||
|
||||
assert [rule["ref"] for rule in merged] == [
|
||||
"existing_rule",
|
||||
f"{MANAGED_RULE_REF_PREFIX}public",
|
||||
f"{MANAGED_RULE_REF_PREFIX}bypass",
|
||||
]
|
||||
assert merged[-1]["action_parameters"]["cache"] is False
|
||||
@@ -138,6 +138,13 @@ def test_scan_terminal_backend_timeout_returns_before_next_proxy_abort():
|
||||
)
|
||||
|
||||
|
||||
def test_deploy_workflow_applies_cloudflare_rules_when_token_is_available():
|
||||
workflow = (ROOT / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8")
|
||||
|
||||
assert "CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}" in workflow
|
||||
assert "python scripts/configure_cloudflare_free.py --apply" in workflow
|
||||
|
||||
|
||||
def test_probability_engine_uses_enriched_multi_model_snapshot():
|
||||
source = (ROOT / "web" / "analysis_service.py").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ from web.scan_terminal_payloads import (
|
||||
SCAN_PAYLOAD_FULL_RUNWAY_HISTORY_ROWS,
|
||||
)
|
||||
from web.scan_terminal_ranker import build_ranked_scan_terminal_result
|
||||
from web import scan_terminal_city_row
|
||||
from web.scan_terminal_city_row import _build_quick_row
|
||||
from web.routers.scan import router as scan_router
|
||||
from web.scan_terminal_service import _scan_terminal_prewarm_filters
|
||||
@@ -81,6 +82,43 @@ def test_scan_terminal_prewarm_covers_default_api_limit():
|
||||
assert 180 in limits
|
||||
|
||||
|
||||
def test_scan_city_terminal_rows_reuses_persisted_panel_cache(monkeypatch):
|
||||
payload = {
|
||||
"display_name": "Paris",
|
||||
"local_date": "2026-06-10",
|
||||
"local_time": "12:00",
|
||||
"current": {"temp": 18.0},
|
||||
"risk": {},
|
||||
"deb": {"prediction": 20.0},
|
||||
"probabilities": {},
|
||||
"multi_model": {},
|
||||
}
|
||||
|
||||
class _Cache:
|
||||
@staticmethod
|
||||
def get_city_cache(kind, city):
|
||||
assert (kind, city) == ("panel", "paris")
|
||||
return {"payload": payload}
|
||||
|
||||
monkeypatch.setattr(scan_terminal_city_row, "_PANEL_CACHE_DB", _Cache())
|
||||
monkeypatch.setattr(
|
||||
scan_terminal_city_row,
|
||||
"_analyze",
|
||||
lambda *_args, **_kwargs: (_ for _ in ()).throw(
|
||||
AssertionError("non-force scan must not fetch external sources")
|
||||
),
|
||||
)
|
||||
|
||||
result = scan_terminal_city_row._scan_city_terminal_rows(
|
||||
"paris",
|
||||
{"market_type": "maxtemp"},
|
||||
force_refresh=False,
|
||||
)
|
||||
|
||||
assert result["city"] == "paris"
|
||||
assert result["rows"][0]["current_temp"] == 18.0
|
||||
|
||||
|
||||
def test_scan_router_does_not_expose_terminal_ai_endpoint():
|
||||
routes = {
|
||||
getattr(route, "path", None): getattr(route, "methods", set())
|
||||
|
||||
@@ -3398,6 +3398,41 @@ def test_scan_terminal_cold_requests_start_background_build_without_blocking(mon
|
||||
assert all("初始化" in result["stale_reason"] or "刷新中" in result["stale_reason"] for result in results)
|
||||
|
||||
|
||||
def test_scan_terminal_background_refresh_reuses_cached_city_data(monkeypatch):
|
||||
filters = {"scan_mode": "tradable", "limit": 17, "min_edge_pct": 6.75}
|
||||
calls = []
|
||||
|
||||
monkeypatch.setattr(
|
||||
scan_terminal_service,
|
||||
"mark_scan_terminal_refreshing",
|
||||
lambda _filters: True,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
scan_terminal_service,
|
||||
"clear_scan_terminal_refreshing",
|
||||
lambda _filters: None,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
scan_terminal_service,
|
||||
"_build_scan_terminal_payload_singleflight",
|
||||
lambda filters_arg, *, force_refresh=False: calls.append(
|
||||
(dict(filters_arg), force_refresh)
|
||||
),
|
||||
)
|
||||
|
||||
class _ImmediateThread:
|
||||
def __init__(self, *, target, name, daemon):
|
||||
self._target = target
|
||||
|
||||
def start(self):
|
||||
self._target()
|
||||
|
||||
monkeypatch.setattr(scan_terminal_service.threading, "Thread", _ImmediateThread)
|
||||
|
||||
assert scan_terminal_service._start_scan_terminal_background_refresh(filters) is True
|
||||
assert calls == [(filters, False)]
|
||||
|
||||
|
||||
def test_scan_terminal_nonforce_ignores_ancient_success_snapshot(monkeypatch):
|
||||
filters = {"scan_mode": "tradable", "limit": 17, "min_edge_pct": 6.75}
|
||||
old_success_t = 1780839484.0
|
||||
|
||||
Reference in New Issue
Block a user