mirror of
https://github.com/chibicitiberiu/ytsm.git
synced 2024-02-24 05:43:31 +00:00
165 lines
5.0 KiB
Python
165 lines
5.0 KiB
Python
import pytest
|
|
import logging
|
|
import sys
|
|
import collections
|
|
from datetime import datetime, timedelta
|
|
|
|
from googleapiclient.errors import HttpError
|
|
|
|
from pytaw import YouTube
|
|
from pytaw.youtube import Resource, Video, AttributeDef
|
|
|
|
|
|
logging.basicConfig(stream=sys.stdout) # show log output when run with pytest -s
|
|
log = logging.getLogger(__name__)
|
|
log.setLevel(logging.INFO)
|
|
|
|
|
|
@pytest.fixture
|
|
def youtube():
|
|
"""A YouTube instance initialised with a developer key loaded from config.ini"""
|
|
return YouTube()
|
|
|
|
|
|
@pytest.fixture
|
|
def video(youtube):
|
|
"""A Video instance for the classic video 'Me at the zoo'"""
|
|
return youtube.video('jNQXAC9IVRw')
|
|
|
|
|
|
@pytest.fixture
|
|
def channel(youtube):
|
|
"""A Channel instance for the 'YouTube Help' channel"""
|
|
return youtube.channel('UCMDQxm7cUx3yXkfeHa5zJIQ')
|
|
|
|
|
|
@pytest.fixture
|
|
def search(youtube):
|
|
"""A ListResponse instance corresponding to a search for the query 'python'"""
|
|
return youtube.search()
|
|
|
|
|
|
@pytest.fixture
|
|
def video_search(youtube):
|
|
"""A ListResponse instance corresponding to a video search for the query 'python'"""
|
|
return youtube.search(q='python', type='video')
|
|
|
|
|
|
@pytest.fixture
|
|
def video_search_array(youtube):
|
|
"""An array of video searches with a wide range of results (zero to millions)."""
|
|
one_minute_ago = datetime.utcnow() - timedelta(minutes=1)
|
|
five_minutes_ago = datetime.utcnow() - timedelta(minutes=5)
|
|
return [
|
|
#
|
|
# no results
|
|
youtube.search(q='minecraft', type='video', publishedBefore=datetime(2000, 1, 1)),
|
|
#
|
|
# less than 100 results
|
|
youtube.search(q='minecraft', type='video', publishedBefore=datetime(2005, 7, 1)),
|
|
#
|
|
# over 100 results
|
|
youtube.search(q='minecraft', type='video', publishedBefore=datetime(2006, 1, 1)),
|
|
#
|
|
# variable number of results (hundreds or thousands...?)
|
|
youtube.search(q='minecraft', type='video', publishedAfter=one_minute_ago),
|
|
youtube.search(q='minecraft', type='video', publishedAfter=five_minutes_ago),
|
|
#
|
|
# over a million results
|
|
youtube.search(q='minecraft', type='video'),
|
|
youtube.search(q='minecraft'),
|
|
]
|
|
|
|
|
|
class TestResource:
|
|
|
|
def test_equality(self, search):
|
|
a = search[0]
|
|
b = search[0]
|
|
c = search[1]
|
|
assert a == b
|
|
assert a != c
|
|
|
|
def test_unknown_attribute(self, video):
|
|
with pytest.raises(AttributeError):
|
|
_ = video.attribute_name_which_definitely_will_never_exist
|
|
|
|
def test_unknown_part_in_attributedef(self, video):
|
|
video.ATTRIBUTE_DEFS['x'] = AttributeDef('nonexistant_part', 'x')
|
|
with pytest.raises(HttpError):
|
|
_ = video.x
|
|
|
|
def test_unknown_attribute_name_in_attributedef(self, video):
|
|
video.ATTRIBUTE_DEFS['x'] = AttributeDef('snippet', 'nonexistant_attribute')
|
|
assert video.x is None
|
|
|
|
|
|
class TestVideo:
|
|
|
|
def test_bad_video_id(self, youtube):
|
|
video = youtube.video('not_a_valid_youtube_video_id')
|
|
assert video is None
|
|
|
|
def test_title(self, video):
|
|
assert video.title == "Me at the zoo"
|
|
|
|
def test_published_at(self, video):
|
|
assert video.published_at.isoformat() == '2005-04-24T03:31:52+00:00'
|
|
|
|
def test_n_views(self, video):
|
|
assert video.n_views > int(40e6)
|
|
|
|
def test_tags(self, video):
|
|
assert video.tags == ['jawed', 'karim', 'elephant', 'zoo', 'youtube', 'first', 'video']
|
|
|
|
def test_duration(self, video):
|
|
assert video.duration.total_seconds() == 19
|
|
|
|
|
|
class TestChannel:
|
|
|
|
def test_title(self, channel):
|
|
assert channel.title == "YouTube Help"
|
|
|
|
|
|
class TestSearch:
|
|
|
|
def test_video_search_returns_a_video(self, video_search):
|
|
assert isinstance(video_search[0], Video)
|
|
|
|
def test_video_search_has_many_results(self, video_search):
|
|
# make video_search unlazy (populate pageInfo attributes)
|
|
_ = video_search[0]
|
|
assert video_search.total_results > 10000
|
|
|
|
def test_search_iteration(self, search):
|
|
"""Simply iterate over a search, creating all resources, to check for exceptions."""
|
|
for resource in search:
|
|
log.debug(resource)
|
|
|
|
|
|
class TestListResponse:
|
|
|
|
def test_if_iterable(self, search):
|
|
assert isinstance(search, collections.Iterator)
|
|
|
|
def test_integer_indexing(self, search):
|
|
assert isinstance(search[0], Resource)
|
|
|
|
def test_slice_indexing(self, search):
|
|
assert isinstance(search[1:3], list)
|
|
|
|
def test_full_listing_iteration(self, video_search_array):
|
|
"""Iterate over all search results to check no exceptions are raised when paging etc.
|
|
|
|
Even if millions of results are found, the API will never return more than 500 (by
|
|
design), so we're okay to just bang right through the search results generator for the
|
|
whole array of video searches.
|
|
|
|
"""
|
|
for i, search in enumerate(video_search_array):
|
|
c = 0
|
|
for _ in search:
|
|
c += 1
|
|
|
|
log.debug(f"checked first {c} results (search #{i})") |