feat(toolbox): GET /admin/ad-stats contextual metrics (ref #656)

This commit is contained in:
CyberMind-FR 2026-06-18 13:09:39 +02:00
parent 96836d865c
commit e23fa9545e
2 changed files with 43 additions and 0 deletions

View File

@ -2429,6 +2429,13 @@ async def admin_protective() -> dict:
return out
@router.get("/admin/ad-stats")
async def admin_ad_stats(hours: int = 24) -> dict:
"""Contextual ad-block metrics for the #ads tab (read-only, kbin-safe)."""
h = max(1, min(int(hours if hours is not None else 24), 168))
return store.ad_stats(hours=h)
@router.get("/admin/ghost")
async def admin_ghost() -> dict:
"""#566 — ad/banner ghoster savings (R3+/R4). Read-only counters."""

View File

@ -0,0 +1,36 @@
# SPDX-License-Identifier: LicenseRef-CMSD-1.0
"""Tests for GET /admin/ad-stats (Task 4 ref #656)."""
import asyncio
from secubox_toolbox import api, store
_CANNED = {
"window_hours": 24,
"total_blocked": 42,
"total_bytes": 63000,
"by_action": {"block": 42, "silent": 5},
"top_hosts": [{"host": "ads.example.com", "hits": 42, "bytes": 63000}],
"top_sites": [{"site": "cnn.com", "hits": 42}],
}
def test_ad_stats_returns_store_data(monkeypatch):
monkeypatch.setattr(store, "ad_stats", lambda hours=24, **kw: dict(_CANNED))
result = asyncio.run(api.admin_ad_stats(hours=24))
assert result["total_blocked"] == 42
assert result["by_action"]["block"] == 42
assert result["top_hosts"][0]["host"] == "ads.example.com"
def test_ad_stats_clamps_hours(monkeypatch):
captured = {}
def fake_stats(hours=24, **kw):
captured["hours"] = hours
return dict(_CANNED)
monkeypatch.setattr(store, "ad_stats", fake_stats)
asyncio.run(api.admin_ad_stats(hours=0))
assert captured["hours"] == 1 # clamped to min=1
asyncio.run(api.admin_ad_stats(hours=9999))
assert captured["hours"] == 168 # clamped to max=168