diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..45e4b57 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: python + +python: + - "2.6" + - "2.7" + - "3.3" + - "3.4" + - "3.5" + - "3.6" + - "nightly" + +install: + - pip install -e . + +script: py.test diff --git a/tests/test_api.py b/tests/test_api.py new file mode 100644 index 0000000..0c0adc8 --- /dev/null +++ b/tests/test_api.py @@ -0,0 +1,64 @@ +# import pytest +import requests + +from toot import App, User, create_app, login, CLIENT_NAME, CLIENT_WEB, SCOPES + + +class MockResponse: + def __init__(self, response_data={}): + self.response_data = response_data + + def raise_for_status(self): + pass + + def json(self): + return self.response_data + + +def test_create_app(monkeypatch): + def mock_post(url, data): + assert url == 'https://bigfish.software/api/v1/apps' + assert data == { + 'website': CLIENT_WEB, + 'client_name': CLIENT_NAME, + 'scopes': SCOPES, + 'redirect_uris': 'urn:ietf:wg:oauth:2.0:oob' + } + return MockResponse({ + 'client_id': 'foo', + 'client_secret': 'bar', + }) + + monkeypatch.setattr(requests, 'post', mock_post) + + app = create_app('https://bigfish.software') + + assert isinstance(app, App) + assert app.client_id == 'foo' + assert app.client_secret == 'bar' + + +def test_login(monkeypatch): + app = App('https://bigfish.software', 'foo', 'bar') + + def mock_post(url, data): + assert url == 'https://bigfish.software/oauth/token' + assert data == { + 'grant_type': 'password', + 'client_id': app.client_id, + 'client_secret': app.client_secret, + 'username': 'user', + 'password': 'pass', + 'scope': SCOPES, + } + return MockResponse({ + 'access_token': 'xxx', + }) + + monkeypatch.setattr(requests, 'post', mock_post) + + user = login(app, 'user', 'pass') + + assert isinstance(user, User) + assert user.username == 'user' + assert user.access_token == 'xxx' diff --git a/tests/test_console.py b/tests/test_console.py new file mode 100644 index 0000000..2b1249a --- /dev/null +++ b/tests/test_console.py @@ -0,0 +1,75 @@ +import pytest +import requests +import sys + +from toot import User, App +from toot.console import cmd_post_status, ConsoleError + +from tests.utils import MockResponse + +app = App('https://habunek.com', 'foo', 'bar') +user = User('ivan@habunek.com', 'xxx') + + +def test_post_status_defaults(monkeypatch): + def mock_prepare(request): + assert request.method == 'POST' + assert request.url == 'https://habunek.com/api/v1/statuses' + assert request.data == { + 'status': '"Hello world"', + 'visibility': 'public', + 'media_ids[]': None, + } + + def mock_send(*args): + return MockResponse({ + 'url': 'http://ivan.habunek.com/' + }) + + monkeypatch.setattr(requests.Request, 'prepare', mock_prepare) + monkeypatch.setattr(requests.Session, 'send', mock_send) + + sys.argv = ['toot', 'post', '"Hello world"'] + cmd_post_status(app, user) + + +def test_post_status_with_options(monkeypatch): + def mock_prepare(request): + assert request.method == 'POST' + assert request.url == 'https://habunek.com/api/v1/statuses' + assert request.data == { + 'status': '"Hello world"', + 'visibility': 'unlisted', + 'media_ids[]': None, + } + + def mock_send(*args): + return MockResponse({ + 'url': 'http://ivan.habunek.com/' + }) + + monkeypatch.setattr(requests.Request, 'prepare', mock_prepare) + monkeypatch.setattr(requests.Session, 'send', mock_send) + + sys.argv = ['toot', 'post', '"Hello world"', + '--visibility', 'unlisted'] + + cmd_post_status(app, user) + + +def test_post_status_invalid_visibility(monkeypatch): + sys.argv = ['toot', 'post', '"Hello world"', + '--visibility', 'foo'] + + with pytest.raises(ConsoleError) as ex: + cmd_post_status(app, user) + assert str(ex.value) == "Invalid visibility value given: 'foo'" + + +def test_post_status_invalid_media(monkeypatch): + sys.argv = ['toot', 'post', '"Hello world"', + '--media', 'does_not_exist.jpg'] + + with pytest.raises(ConsoleError) as ex: + cmd_post_status(app, user) + assert str(ex.value) == "File does not exist: does_not_exist.jpg" diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 0000000..765dc7e --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,10 @@ + +class MockResponse: + def __init__(self, response_data={}): + self.response_data = response_data + + def raise_for_status(self): + pass + + def json(self): + return self.response_data diff --git a/toot/__init__.py b/toot/__init__.py index 7a81793..d3c7e7f 100644 --- a/toot/__init__.py +++ b/toot/__init__.py @@ -9,6 +9,11 @@ User = namedtuple('User', ['username', 'access_token']) DEFAULT_INSTANCE = 'mastodon.social' +CLIENT_NAME = 'toot - Mastodon CLI Interface' +CLIENT_WEB = 'https://github.com/ihabunek/toot' + +SCOPES = 'read write follow' + logger = logging.getLogger('toot') @@ -59,7 +64,7 @@ def create_app(base_url): response = requests.post(url, { 'client_name': 'toot - Mastodon CLI Interface', 'redirect_uris': 'urn:ietf:wg:oauth:2.0:oob', - 'scopes': 'read write', + 'scopes': SCOPES, 'website': 'https://github.com/ihabunek/toot', }) @@ -81,7 +86,7 @@ def login(app, username, password): 'client_secret': app.client_secret, 'username': username, 'password': password, - 'scope': 'read write', + 'scope': SCOPES, }) response.raise_for_status()