Extract auth code to own file, add some tests - toot - Unnamed repository; edit this file 'description' to name the repository.
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) LICENSE
---
(DIR) commit a50ffe62c33698b9a1bf1211149cdfb1aaeca48e
(DIR) parent 787e0d28b4cd9d77d7948da80874d17942e9c330
(HTM) Author: Ivan Habunek <ivan@habunek.com>
Date: Sat, 30 Dec 2017 12:52:55 +0100
Extract auth code to own file, add some tests
Diffstat:
tests/test_auth.py | 57 +++++++++++++++++++++++++++++++
tests/utils.py | 4 ++++
toot/auth.py | 101 +++++++++++++++++++++++++++++++
toot/commands.py | 97 +------------------------------
4 files changed, 165 insertions(+), 94 deletions(-)
---
(DIR) diff --git a/tests/test_auth.py b/tests/test_auth.py
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+
+from toot import App, User, api, config, auth
+from tests.utils import retval
+
+
+def test_register_app(monkeypatch):
+ app_data = {'id': 100, 'client_id': 'cid', 'client_secret': 'cs'}
+
+ def assert_app(app):
+ assert isinstance(app, App)
+ assert app.instance == "foo.bar"
+ assert app.base_url == "https://foo.bar"
+ assert app.client_id == "cid"
+ assert app.client_secret == "cs"
+
+ monkeypatch.setattr(api, 'create_app', retval(app_data))
+ monkeypatch.setattr(config, 'save_app', assert_app)
+
+ app = auth.register_app("foo.bar")
+ assert_app(app)
+
+
+def test_create_app_from_config(monkeypatch):
+ """When there is saved config, it's returned"""
+ monkeypatch.setattr(config, 'load_app', retval("loaded app"))
+ app = auth.create_app_interactive("bezdomni.net")
+ assert app == 'loaded app'
+
+
+def test_create_app_registered(monkeypatch):
+ """When there is no saved config, a new app is registered"""
+ monkeypatch.setattr(config, 'load_app', retval(None))
+ monkeypatch.setattr(auth, 'register_app', retval("registered app"))
+
+ app = auth.create_app_interactive("bezdomni.net")
+ assert app == 'registered app'
+
+
+def test_create_user(monkeypatch):
+ app = App(4, 5, 6, 7)
+
+ def assert_user(user):
+ assert isinstance(user, User)
+ assert user.instance == app.instance
+ assert user.username == 2
+ assert user.access_token == 3
+
+ monkeypatch.setattr(config, 'save_user', assert_user)
+
+ user = auth.create_user(app, 2, 3)
+
+ assert_user(user)
+
+#
+# TODO: figure out how to mock input so the rest can be tested
+#
(DIR) diff --git a/tests/utils.py b/tests/utils.py
@@ -10,3 +10,7 @@ class MockResponse:
def json(self):
return self.response_data
+
+
+def retval(val):
+ return lambda *args, **kwargs: val
(DIR) diff --git a/toot/auth.py b/toot/auth.py
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+
+import webbrowser
+
+from builtins import input
+from getpass import getpass
+
+from toot import api, config, DEFAULT_INSTANCE, User, App, ConsoleError
+from toot.output import print_out
+
+
+def register_app(instance):
+ print_out("Registering application with <green>{}</green>".format(instance))
+
+ try:
+ response = api.create_app(instance)
+ except Exception:
+ raise ConsoleError("Registration failed. Did you enter a valid instance?")
+
+ base_url = 'https://' + instance
+
+ app = App(instance, base_url, response['client_id'], response['client_secret'])
+ path = config.save_app(app)
+ print_out("Application tokens saved to: <green>{}</green>\n".format(path))
+
+ return app
+
+
+def create_app_interactive(instance=None):
+ if not instance:
+ print_out("Choose an instance [<green>{}</green>]: ".format(DEFAULT_INSTANCE), end="")
+ instance = input()
+ if not instance:
+ instance = DEFAULT_INSTANCE
+
+ return config.load_app(instance) or register_app(instance)
+
+
+def create_user(app, email, access_token):
+ user = User(app.instance, email, access_token)
+ path = config.save_user(user)
+
+ print_out("Access token saved to: <green>{}</green>".format(path))
+
+ return user
+
+
+def login_interactive(app, email=None):
+ print_out("Log in to <green>{}</green>".format(app.instance))
+
+ if email:
+ print_out("Email: <green>{}</green>".format(email))
+
+ while not email:
+ email = input('Email: ')
+
+ password = getpass('Password: ')
+
+ try:
+ print_out("Authenticating...")
+ response = api.login(app, email, password)
+ except api.ApiError:
+ raise ConsoleError("Login failed")
+
+ return create_user(app, email, response['access_token'])
+
+
+BROWSER_LOGIN_EXPLANATION = """
+This authentication method requires you to log into your Mastodon instance
+in your browser, where you will be asked to authorize <yellow>toot</yellow> to access
+your account. When you do, you will be given an <yellow>authorization code</yellow>
+which you need to paste here.
+"""
+
+
+def login_browser_interactive(app):
+ url = api.get_browser_login_url(app)
+
+ print_out(BROWSER_LOGIN_EXPLANATION)
+
+ print_out("This is the login URL:")
+ print_out(url)
+ print_out("")
+
+ yesno = input("Open link in default browser? [Y/n]")
+ if not yesno or yesno.lower() == 'y':
+ webbrowser.open(url)
+
+ authorization_code = ""
+ while not authorization_code:
+ authorization_code = input("Authorization code: ")
+
+ print_out("\nRequesting access token...")
+ response = api.request_access_token(app, authorization_code)
+
+ # TODO: user email is not available in this workflow, maybe change the User
+ # to store the username instead? Currently set to "unknown" since it's not
+ # used anywhere.
+ email = "unknown"
+
+ return create_user(app, email, response['access_token'])
(DIR) diff --git a/toot/commands.py b/toot/commands.py
@@ -1,75 +1,16 @@
# -*- coding: utf-8 -*-
-import webbrowser
-
from bs4 import BeautifulSoup
-from builtins import input
from datetime import datetime
from itertools import zip_longest
-from getpass import getpass
from itertools import chain
from textwrap import TextWrapper
-from toot import api, config, DEFAULT_INSTANCE, User, App, ConsoleError
+from toot import api, config, ConsoleError
+from toot.auth import login_interactive, login_browser_interactive, create_app_interactive
from toot.output import print_out, print_instance, print_account, print_search_results
-def register_app(instance):
- print_out("Registering application with <green>{}</green>".format(instance))
-
- try:
- response = api.create_app(instance)
- except:
- raise ConsoleError("Registration failed. Did you enter a valid instance?")
-
- base_url = 'https://' + instance
-
- app = App(instance, base_url, response['client_id'], response['client_secret'])
- path = config.save_app(app)
- print_out("Application tokens saved to: <green>{}</green>\n".format(path))
-
- return app
-
-
-def create_app_interactive(instance=None):
- if not instance:
- print_out("Choose an instance [<green>{}</green>]: ".format(DEFAULT_INSTANCE), end="")
- instance = input()
- if not instance:
- instance = DEFAULT_INSTANCE
-
- return config.load_app(instance) or register_app(instance)
-
-
-def create_user(app, email, access_token):
- user = User(app.instance, email, access_token)
- path = config.save_user(user)
-
- print_out("Access token saved to: <green>{}</green>".format(path))
-
- return user
-
-
-def login_interactive(app, email=None):
- print_out("Log in to <green>{}</green>".format(app.instance))
-
- if email:
- print_out("Email: <green>{}</green>".format(email))
-
- while not email:
- email = input('Email: ')
-
- password = getpass('Password: ')
-
- try:
- print_out("Authenticating...")
- response = api.login(app, email, password)
- except api.ApiError:
- raise ConsoleError("Login failed")
-
- return create_user(app, email, response['access_token'])
-
-
def _print_timeline(item):
def wrap_text(text, width):
wrapper = TextWrapper(width=width, break_long_words=False, break_on_hyphens=False)
@@ -156,41 +97,9 @@ def login(app, user, args):
print_out("<green>✓ Successfully logged in.</green>")
-BROWSER_LOGIN_EXPLANATION = """
-This authentication method requires you to log into your Mastodon instance
-in your browser, where you will be asked to authorize <yellow>toot</yellow> to access
-your account. When you do, you will be given an <yellow>authorization code</yellow>
-which you need to paste here.
-"""
-
-
def login_browser(app, user, args):
app = create_app_interactive(instance=args.instance)
- url = api.get_browser_login_url(app)
-
- print_out(BROWSER_LOGIN_EXPLANATION)
-
- print_out("This is the login URL:")
- print_out(url)
- print_out("")
-
- yesno = input("Open link in default browser? [Y/n]")
- if not yesno or yesno.lower() == 'y':
- webbrowser.open(url)
-
- authorization_code = ""
- while not authorization_code:
- authorization_code = input("Authorization code: ")
-
- print_out("\nRequesting access token...")
- response = api.request_access_token(app, authorization_code)
-
- # TODO: user email is not available in this workflow, maybe change the User
- # to store the username instead? Currently set to "unknown" since it's not
- # used anywhere.
- email = "unknown"
-
- create_user(app, email, response['access_token'])
+ login_browser_interactive(app)
print_out()
print_out("<green>✓ Successfully logged in.</green>")