diff --git a/backend/tests/test_video_length.py b/backend/tests/test_video_length.py index 85ecebc5..5dbdda47 100644 --- a/backend/tests/test_video_length.py +++ b/backend/tests/test_video_length.py @@ -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 ``. + if any(_reads_raw(n) and id(n) not in in_a_function for n in ast.walk(tree)): + found.add((rel, "")) 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)