feat: add typed qBittorrent scheduled polling
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from types import SimpleNamespace
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
@@ -22,6 +23,7 @@ from media_library_viewer_api.widgets.sources import (
|
||||
)
|
||||
|
||||
TEST_KEY = Fernet.generate_key().decode()
|
||||
PROMQL_REQUIRED_ERROR = "promql is required"
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
@@ -479,6 +481,7 @@ def test_jellyfin_definition_has_now_playing_widget():
|
||||
from media_library_viewer_api.integrations.registry import get_service_definition
|
||||
|
||||
definition = get_service_definition("jellyfin")
|
||||
assert definition is not None
|
||||
kinds = {wk.kind for wk in definition.widget_kinds}
|
||||
assert "now_playing" in kinds
|
||||
assert "activity" in kinds
|
||||
@@ -543,7 +546,7 @@ async def test_prometheus_chart_adapter_requires_promql():
|
||||
secrets={"grafana_api_key": "key"},
|
||||
)
|
||||
result = await adapter.fetch(service, "chart", {"promql": ""})
|
||||
assert result == {"error": "promql is required"}
|
||||
assert result == {"error": PROMQL_REQUIRED_ERROR}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -639,7 +642,7 @@ async def test_jellyfin_activity_shows_all_sessions():
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def widget_ref_client(monkeypatch):
|
||||
def widget_ref_client(monkeypatch, request):
|
||||
"""TestClient with an isolated SettingsStore + encryption key."""
|
||||
monkeypatch.setenv(
|
||||
"MANAGE_ENCRYPTION_KEY",
|
||||
@@ -659,8 +662,8 @@ def widget_ref_client(monkeypatch):
|
||||
|
||||
app.dependency_overrides[get_settings_store] = get_store_override
|
||||
client = TestClient(app)
|
||||
yield client, store
|
||||
app.dependency_overrides.pop(get_settings_store, None)
|
||||
request.addfinalizer(lambda: app.dependency_overrides.pop(get_settings_store, None))
|
||||
return client, store
|
||||
|
||||
|
||||
def test_widget_reference_lifecycle(widget_ref_client):
|
||||
@@ -936,7 +939,7 @@ async def test_prometheus_gauge_adapter_requires_promql():
|
||||
secrets={"grafana_api_key": "key"},
|
||||
)
|
||||
result = await adapter.fetch(service, "gauge", {"promql": ""})
|
||||
assert result == {"error": "promql is required"}
|
||||
assert result == {"error": PROMQL_REQUIRED_ERROR}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -1038,7 +1041,7 @@ async def test_prometheus_mean_adapter_requires_promql():
|
||||
secrets={"grafana_api_key": "key"},
|
||||
)
|
||||
result = await adapter.fetch(service, "mean", {"promql": ""})
|
||||
assert result == {"error": "promql is required"}
|
||||
assert result == {"error": PROMQL_REQUIRED_ERROR}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -1139,8 +1142,8 @@ async def test_qbittorrent_active_filters_dl_ul_only():
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_qbittorrent_speed_appends_and_returns_series(tmp_path):
|
||||
"""Speed kind appends a sample and returns {series} with two labeled series."""
|
||||
async def test_qbittorrent_speed_reads_samples_without_polling(tmp_path):
|
||||
"""Speed kind reads persisted samples and never calls qBittorrent itself."""
|
||||
from media_library_viewer_api.services.qbittorrent_store import QBITTORRENT_CONCERN, QbittorrentSampleStore
|
||||
from media_library_viewer_api.services.service_data import ServiceDataHarness
|
||||
from media_library_viewer_api.widgets.sources import QbittorrentWidgetSource
|
||||
@@ -1163,15 +1166,18 @@ async def test_qbittorrent_speed_appends_and_returns_series(tmp_path):
|
||||
patch("media_library_viewer_api.widgets.sources.QbittorrentSampleStore") as mock_store_cls,
|
||||
):
|
||||
mock_client.return_value.maindata.return_value = _fake_qbit_maindata()
|
||||
# Wire the mock store to a real isolated store
|
||||
# Wire the mock store to a real isolated store and seed headless data.
|
||||
real_store = QbittorrentSampleStore(harness)
|
||||
real_store.append("svc-speed", round(time.time()), 500000, 1000)
|
||||
mock_store_cls.return_value = real_store
|
||||
result = await adapter.fetch(service, "speed", {})
|
||||
|
||||
mock_client.assert_not_called()
|
||||
|
||||
assert "series" in result
|
||||
labels = [s["label"] for s in result["series"]]
|
||||
assert labels == ["download", "upload"]
|
||||
# The sample just appended should be present
|
||||
# The scheduler-supplied sample should be present.
|
||||
dl_points = result["series"][0]["points"]
|
||||
assert len(dl_points) >= 1
|
||||
# timestamps multiplied by 1000 for JS epoch
|
||||
|
||||
Reference in New Issue
Block a user