test(feed): an off-catalog video's length, and async readers of the raw column

The YouTube lookup's length had no test, and the guard on who reads the raw duration skipped async
functions and module-level code.
This commit is contained in:
2026-11-07 21:48:38 +01:00
parent bb91d0a22d
commit 4ed07388e8
+37 -2
View File
@@ -95,6 +95,32 @@ def test_a_years_long_archive_has_no_length_in_the_feed_or_a_playlist(db, make_u
assert one.json()["duration_seconds"] is None
def test_an_off_catalog_archive_has_no_length_either(db, make_user, as_user, monkeypatch):
# A video not in the catalog is looked up on YouTube (videos.list); its length is judged the same.
from app.routes import feed as feed_routes
class Yt:
def __init__(self, *_):
pass
def __enter__(self):
return self
def __exit__(self, *exc):
return False
def get_videos(self, ids):
hours = {"offyears": YEARS // HOUR, "offshort": 1}[ids[0]]
return [
{"snippet": {}, "statistics": {}, "contentDetails": {"duration": f"PT{hours}H"}}
]
monkeypatch.setattr(feed_routes, "YouTubeClient", Yt)
client = as_user(make_user())
assert client.get("/api/videos/offyears").json()["duration_seconds"] is None
assert client.get("/api/videos/offshort").json()["duration_seconds"] == HOUR
def test_the_totals_leave_it_out_and_count_the_live_ones(db, make_user, as_user):
user = make_user()
pl = _seed(db, user)
@@ -165,15 +191,24 @@ def _raw_reads() -> set[tuple[str, str]]:
continue
rel = path.relative_to(root).as_posix()
tree = ast.parse(path.read_text(encoding="utf-8"))
in_a_function: set[int] = set()
for fn in ast.walk(tree):
if not isinstance(fn, ast.FunctionDef):
if not isinstance(fn, (ast.FunctionDef, ast.AsyncFunctionDef)):
continue
for node in ast.walk(fn):
if isinstance(node, ast.Attribute) and node.attr == "duration_seconds":
if _reads_raw(node):
found.add((rel, fn.name))
in_a_function.add(id(node))
# A read outside every def (module or class level, a lambda there) is named `<module>`.
if any(_reads_raw(n) and id(n) not in in_a_function for n in ast.walk(tree)):
found.add((rel, "<module>"))
return found
def _reads_raw(node: ast.AST) -> bool:
return isinstance(node, ast.Attribute) and node.attr == "duration_seconds"
def test_only_the_listed_readers_take_the_raw_column():
found = _raw_reads()
stray = sorted(found - RAW_READERS)