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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user