fix(logs): keep polling reads and a channel-less account out of the warnings
The reads the frontend polls (unread counts, /api/me, the download index, sync status, subtitle progress) are logged at DEBUG: at INFO they were ~95% of the stored log on prod. A scheduler job failure and a playlist sync failure carry event codes, and a Google account without a YouTube channel has an empty playlist list instead of a warning every six hours.
This commit is contained in:
@@ -219,6 +219,14 @@ def _client() -> TestClient:
|
||||
raise HTTPException(status_code=400, detail={"code": "bad_cursor"})
|
||||
return {"ok": True}
|
||||
|
||||
@app.get("/api/messages/unread_count")
|
||||
def _unread():
|
||||
return {"count": 0}
|
||||
|
||||
@app.post("/api/messages/unread_count")
|
||||
def _unread_write():
|
||||
return {"ok": True}
|
||||
|
||||
@app.get("/boom")
|
||||
def _boom():
|
||||
raise RuntimeError("kaboom")
|
||||
@@ -323,6 +331,30 @@ def test_the_log_consoles_own_reads_are_debug_but_its_failures_are_not(sink):
|
||||
assert sink.objects()[-1]["level"] == "WARNING"
|
||||
|
||||
|
||||
def test_a_polled_read_is_debug_a_write_to_the_same_route_is_not(sink):
|
||||
# One open tab polled the unread counts, /api/me and the download index every 30 s: at INFO
|
||||
# that was ~95% of the stored log on prod.
|
||||
http = logging.getLogger("siftlode.http")
|
||||
http.setLevel(logs.TRACE)
|
||||
client = _client()
|
||||
client.get("/api/messages/unread_count")
|
||||
assert sink.objects()[-1]["level"] == "DEBUG"
|
||||
client.post("/api/messages/unread_count")
|
||||
assert sink.objects()[-1]["level"] == "INFO"
|
||||
client.get("/api/videos/abc")
|
||||
assert sink.objects()[-1]["level"] == "INFO"
|
||||
|
||||
|
||||
def test_every_polled_route_is_a_route_the_app_has():
|
||||
# The list names route TEMPLATES; one renamed on the router side would silently fall back to
|
||||
# INFO, so the list is held to the app's own routes.
|
||||
from app.main import app
|
||||
|
||||
# The schema, not `app.routes`: FastAPI 0.141 keeps an included router behind a wrapper there.
|
||||
gets = {path for path, ops in app.openapi()["paths"].items() if "get" in ops}
|
||||
assert logs._POLLED_ROUTES - gets == set()
|
||||
|
||||
|
||||
def test_a_failing_request_is_logged_by_status_and_an_unhandled_error_is_logged_at_all(sink):
|
||||
client = _client()
|
||||
client.get("/api/videos/missing/deeper") # 404 from the router
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
"""A Google account without a YouTube channel: its `mine` playlists read answers 404
|
||||
`channelNotFound`. That is an empty list, not a failed sync — it came back as a warning every six
|
||||
hours on prod for an account that will never have a channel."""
|
||||
|
||||
import logging
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from app.youtube import client as yt
|
||||
|
||||
|
||||
def _client(monkeypatch, status: int, reason: str) -> yt.YouTubeClient:
|
||||
resp = httpx.Response(
|
||||
status,
|
||||
json={"error": {"code": status, "errors": [{"reason": reason, "message": "prose"}]}},
|
||||
request=httpx.Request("GET", f"{yt.API_BASE}/playlists"),
|
||||
)
|
||||
c = yt.YouTubeClient.__new__(yt.YouTubeClient) # no HTTP client: `_send` is the stub
|
||||
c.db = None
|
||||
monkeypatch.setattr(yt.sysconfig, "get_str", lambda db, key: "")
|
||||
monkeypatch.setattr(yt.quota, "record_usage", lambda cost: None)
|
||||
monkeypatch.setattr(c, "_access_token", lambda: "t")
|
||||
monkeypatch.setattr(c, "_send", lambda *a, **k: resp)
|
||||
return c
|
||||
|
||||
|
||||
def test_no_channel_is_an_empty_playlist_list_and_no_warning(monkeypatch, caplog):
|
||||
caplog.set_level(logging.DEBUG, logger="siftlode")
|
||||
assert list(_client(monkeypatch, 404, "channelNotFound").iter_my_playlists()) == []
|
||||
assert not [r for r in caplog.records if r.levelno >= logging.WARNING]
|
||||
|
||||
|
||||
def test_any_other_failure_still_raises_and_warns(monkeypatch, caplog):
|
||||
caplog.set_level(logging.DEBUG, logger="siftlode")
|
||||
with pytest.raises(yt.YouTubeError):
|
||||
list(_client(monkeypatch, 403, "quotaExceeded").iter_my_playlists())
|
||||
assert [r for r in caplog.records if r.levelno == logging.WARNING]
|
||||
Reference in New Issue
Block a user