diff --git a/docs/index.rst b/docs/index.rst index aab633d..d5492c5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -252,6 +252,7 @@ API Reference .. automodule:: spotipy.client :members: :undoc-members: + :special-members: __init__ :show-inheritance: :mod:`oauth2` Module @@ -260,6 +261,7 @@ API Reference .. automodule:: spotipy.oauth2 :members: :undoc-members: + :special-members: __init__ :show-inheritance: :mod:`util` Module @@ -268,6 +270,7 @@ API Reference .. automodule:: spotipy.util :members: :undoc-members: + :special-members: __init__ :show-inheritance: diff --git a/spotipy/client.py b/spotipy/client.py index ec3476d..3a63bb0 100644 --- a/spotipy/client.py +++ b/spotipy/client.py @@ -355,7 +355,7 @@ class Spotify(object): return self._get("users/%s/playlists/%s" % (user, plid), fields=fields) def user_playlist_tracks(self, user, playlist_id=None, fields=None, - limit=100, offset=0): + limit=100, offset=0, market=None): ''' Get full details of the tracks of a playlist owned by a user. Parameters: @@ -364,10 +364,12 @@ class Spotify(object): - fields - which fields to return - limit - the maximum number of tracks to return - offset - the index of the first track to return + - market - an ISO 3166-1 alpha-2 country code. ''' plid = self._get_id('playlist', playlist_id) return self._get("users/%s/playlists/%s/tracks" % (user, plid), - limit=limit, offset=offset, fields=fields) + limit=limit, offset=offset, fields=fields, + market=market) def user_playlist_create(self, user, name, public=True): ''' Creates a playlist for a user diff --git a/tests/test_oauth.py b/tests/test_oauth.py new file mode 100644 index 0000000..996f510 --- /dev/null +++ b/tests/test_oauth.py @@ -0,0 +1,118 @@ +from spotipy.oauth2 import SpotifyOAuth +import json +import io +import unittest + +try: + import unittest.mock as mock +except ImportError: + import mock + +patch = mock.patch +DEFAULT = mock.DEFAULT + + +def _make_fake_token(expires_at, expires_in, scope): + return dict( + expires_at=expires_at, + expires_in=expires_in, + scope=scope, + token_type="Bearer", + refresh_token="REFRESH", + access_token="ACCESS") + + +def _fake_file(): + return mock.Mock(spec_set=io.FileIO) + + +def _token_file(token): + fi = _fake_file() + fi.read.return_value = token + return fi + + +def _make_oauth(*args, **kwargs): + return SpotifyOAuth("CLID", "CLISEC", "REDIR", "STATE", *args, **kwargs) + + +class OAuthCacheTest(unittest.TestCase): + + @patch.multiple(SpotifyOAuth, + _is_token_expired=DEFAULT, _refresh_access_token=DEFAULT) + @patch('spotipy.oauth2.open', create=True) + def test_gets_from_cache_path(self, opener, + _is_token_expired, _refresh_access_token): + scope = "playlist-modify-private" + path = ".cache-username" + tok = _make_fake_token(1, 1, scope) + + opener.return_value = _token_file(json.dumps(tok, ensure_ascii=False)) + _is_token_expired.return_value = False + + spot = _make_oauth(scope, path) + cached_tok = spot.get_cached_token() + + opener.assert_called_with(path) + self.assertIsNotNone(cached_tok) + self.assertEqual(_refresh_access_token.call_count, 0) + + @patch.multiple(SpotifyOAuth, + _is_token_expired=DEFAULT, _refresh_access_token=DEFAULT) + @patch('spotipy.oauth2.open', create=True) + def test_expired_token_refreshes(self, opener, + _is_token_expired, _refresh_access_token): + scope = "playlist-modify-private" + path = ".cache-username" + expired_tok = _make_fake_token(0, None, scope) + fresh_tok = _make_fake_token(1, 1, scope) + + token_file = _token_file(json.dumps(expired_tok, ensure_ascii=False)) + opener.return_value = token_file + _refresh_access_token.return_value = fresh_tok + + spot = _make_oauth(scope, path) + spot.get_cached_token() + + _is_token_expired.assert_called_with(expired_tok) + _refresh_access_token.assert_called_with(expired_tok['refresh_token']) + opener.assert_any_call(path) + + @patch.multiple(SpotifyOAuth, + _is_token_expired=DEFAULT, _refresh_access_token=DEFAULT) + @patch('spotipy.oauth2.open', create=True) + def test_badly_scoped_token_bails(self, opener, + _is_token_expired, _refresh_access_token): + token_scope = "playlist-modify-public" + requested_scope = "playlist-modify-private" + path = ".cache-username" + tok = _make_fake_token(1, 1, token_scope) + + opener.return_value = _token_file(json.dumps(tok, ensure_ascii=False)) + _is_token_expired.return_value = False + + spot = _make_oauth(requested_scope, path) + cached_tok = spot.get_cached_token() + + opener.assert_called_with(path) + self.assertIsNone(cached_tok) + self.assertEqual(_refresh_access_token.call_count, 0) + + @patch('spotipy.oauth2.open', create=True) + def test_saves_to_cache_path(self, opener): + scope = "playlist-modify-private" + path = ".cache-username" + tok = _make_fake_token(1, 1, scope) + + fi = _fake_file() + opener.return_value = fi + + spot = SpotifyOAuth("CLID", "CLISEC", "REDIR", "STATE", scope, path) + spot._save_token_info(tok) + + opener.assert_called_with(path, 'w') + self.assertTrue(fi.write.called) + + +if __name__ == '__main__': + unittest.main() diff --git a/tox.ini b/tox.ini index ffce4a8..705b07e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,7 @@ [tox] envlist = py27,py34 [testenv] -deps=requests +deps= + requests + py27: mock commands=python -m unittest discover tests