Increase coverage % for oauth2client.appengine.

Reviewed in http://codereview.appspot.com/5795070/.
This commit is contained in:
Joe Gregorio
2012-03-14 00:09:33 -04:00
parent d6b1106565
commit 08cdcb829e
8 changed files with 211 additions and 10 deletions

View File

@@ -20,6 +20,14 @@ test:
python runtests.py tests/test_schema.py
python runtests.py tests/test_oauth2client_appengine.py
.PHONY: coverage
coverage:
coverage erase
find tests -name "test_*.py" | xargs --max-args=1 coverage run -a runtests.py
coverage report
coverage html
.PHONY: docs
docs:
cd docs; ./build.sh

View File

@@ -448,7 +448,8 @@ class OAuth2DecoratorFromClientSecrets(OAuth2Decorator):
Args:
filename: string, File name of client secrets.
scope: string, Space separated list of scopes.
scope: string or list of strings, scope(s) of the credentials being
requested.
message: string, A friendly string to display to the user if the
clientsecrets file is missing or invalid. The message may contain HTML and
will be presented on the web interface for any method that uses the
@@ -479,7 +480,8 @@ def oauth2decorator_from_clientsecrets(filename, scope, message=None):
Args:
filename: string, File name of client secrets.
scope: string, Space separated list of scopes.
scope: string or list of strings, scope(s) of the credentials being
requested.
message: string, A friendly string to display to the user if the
clientsecrets file is missing or invalid. The message may contain HTML and
will be presented on the web interface for any method that uses the

View File

@@ -159,7 +159,8 @@ class Credentials(object):
t = type(self)
d = copy.copy(self.__dict__)
for member in strip:
del d[member]
if member in d:
del d[member]
if 'token_expiry' in d and isinstance(d['token_expiry'], datetime.datetime):
d['token_expiry'] = d['token_expiry'].strftime(EXPIRY_FORMAT)
# Add in information we will need later to reconsistitue this instance.
@@ -203,6 +204,19 @@ class Credentials(object):
from_json = getattr(kls, 'from_json')
return from_json(s)
@classmethod
def from_json(cls, s):
"""Instantiate a Credentials object from a JSON description of it. The JSON
should have been produced by calling .to_json() on the object.
Args:
data: dict, A deserialized JSON object.
Returns:
An instance of a Credentials subclass.
"""
return Credentials()
class Flow(object):
"""Base class for all Flow objects."""

View File

@@ -27,9 +27,10 @@ use_library('django', '1.2')
def main():
module = imp.load_source('test', sys.argv[1])
test = unittest.TestLoader().loadTestsFromModule(module)
result = unittest.TextTestRunner(verbosity=1).run(test)
for t in sys.argv[1:]:
module = imp.load_source('test', t)
test = unittest.TestLoader().loadTestsFromModule(module)
result = unittest.TextTestRunner(verbosity=1).run(test)
if __name__ == '__main__':

View File

@@ -0,0 +1,9 @@
{
"web": {
"client_id": "foo_client_id",
"client_secret": "foo_client_secret",
"redirect_uris": [],
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token"
}
}

View File

@@ -0,0 +1,9 @@
{
"web": {
"client_id": "[[INSERT CLIENT ID HERE]]",
"client_secret": "[[INSERT CLIENT SECRET HERE]]",
"redirect_uris": [],
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token"
}
}

View File

@@ -39,7 +39,9 @@ from oauth2client.client import AccessTokenCredentials
from oauth2client.client import AccessTokenCredentialsError
from oauth2client.client import AccessTokenRefreshError
from oauth2client.client import AssertionCredentials
from oauth2client.client import Credentials
from oauth2client.client import FlowExchangeError
from oauth2client.client import MemoryCache
from oauth2client.client import OAuth2Credentials
from oauth2client.client import OAuth2WebServerFlow
from oauth2client.client import OOB_CALLBACK_URN
@@ -47,6 +49,14 @@ from oauth2client.client import VerifyJwtTokenError
from oauth2client.client import _extract_id_token
class CredentialsTests(unittest.TestCase):
def test_to_from_json(self):
credentials = Credentials()
json = credentials.to_json()
restored = Credentials.new_from_json(json)
class OAuth2CredentialsTests(unittest.TestCase):
def setUp(self):
@@ -71,6 +81,7 @@ class OAuth2CredentialsTests(unittest.TestCase):
http = self.credentials.authorize(http)
resp, content = http.request("http://example.com")
self.assertEqual('Bearer 1/3w', content['Authorization'])
self.assertFalse(self.credentials.access_token_expired)
def test_token_refresh_failure(self):
http = HttpMockSequence([
@@ -83,6 +94,7 @@ class OAuth2CredentialsTests(unittest.TestCase):
self.fail("should raise AccessTokenRefreshError exception")
except AccessTokenRefreshError:
pass
self.assertTrue(self.credentials.access_token_expired)
def test_non_401_error_response(self):
http = HttpMockSequence([
@@ -285,5 +297,17 @@ class OAuth2WebServerFlowTest(unittest.TestCase):
self.assertEqual(credentials.id_token, body)
class MemoryCacheTests(unittest.TestCase):
def test_get_set_delete(self):
m = MemoryCache()
self.assertEqual(None, m.get('foo'))
self.assertEqual(None, m.delete('foo'))
m.set('foo', 'bar')
self.assertEqual('bar', m.get('foo'))
m.delete('foo')
self.assertEqual(None, m.get('foo'))
if __name__ == '__main__':
unittest.main()

View File

@@ -25,6 +25,7 @@ __author__ = 'jcgregorio@google.com (Joe Gregorio)'
import base64
import datetime
import httplib2
import os
import time
import unittest
import urlparse
@@ -42,30 +43,51 @@ from apiclient.http import HttpMockSequence
from google.appengine.api import apiproxy_stub
from google.appengine.api import apiproxy_stub_map
from google.appengine.api import app_identity
from google.appengine.api import users
from google.appengine.api import memcache
from google.appengine.api import users
from google.appengine.api.memcache import memcache_stub
from google.appengine.ext import db
from google.appengine.ext import testbed
from google.appengine.runtime import apiproxy_errors
from oauth2client.anyjson import simplejson
from oauth2client.appengine import AppAssertionCredentials
from oauth2client.appengine import FlowProperty
from oauth2client.appengine import CredentialsModel
from oauth2client.appengine import OAuth2Decorator
from oauth2client.appengine import OAuth2Handler
from oauth2client.appengine import StorageByKeyName
from oauth2client.appengine import oauth2decorator_from_clientsecrets
from oauth2client.client import AccessTokenRefreshError
from oauth2client.client import Credentials
from oauth2client.client import FlowExchangeError
from oauth2client.client import OAuth2Credentials
from oauth2client.client import flow_from_clientsecrets
from webtest import TestApp
DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')
def datafile(filename):
return os.path.join(DATA_DIR, filename)
class UserMock(object):
"""Mock the app engine user service"""
def __call__(self):
return self
def user_id(self):
return 'foo_user'
class UserNotLoggedInMock(object):
"""Mock the app engine user service"""
def __call__(self):
return None
class Http2Mock(object):
"""Mock httplib2.Http"""
status = 200
@@ -131,12 +153,41 @@ class TestAppAssertionCredentials(unittest.TestCase):
apiproxy_stub_map.apiproxy.RegisterStub(
'memcache', memcache_stub.MemcacheServiceStub())
scope = "http://www.googleapis.com/scope"
scope = ["http://www.googleapis.com/scope"]
credentials = AppAssertionCredentials(scope)
http = httplib2.Http()
credentials.refresh(http)
self.assertEqual('a_token_123', credentials.access_token)
json = credentials.to_json()
credentials = Credentials.new_from_json(json)
self.assertEqual(scope[0], credentials.scope)
class TestFlowModel(db.Model):
flow = FlowProperty()
class FlowPropertyTest(unittest.TestCase):
def setUp(self):
self.testbed = testbed.Testbed()
self.testbed.activate()
self.testbed.init_datastore_v3_stub()
def tearDown(self):
self.testbed.deactivate()
def test_flow_get_put(self):
instance = TestFlowModel(
flow=flow_from_clientsecrets(datafile('client_secrets.json'), 'foo'),
key_name='foo'
)
instance.put()
retrieved = TestFlowModel.get_by_key_name('foo')
self.assertEqual('foo_client_id', retrieved.flow.client_id)
def _http_request(*args, **kwargs):
resp = httplib2.Response({'status': '200'})
@@ -206,7 +257,6 @@ class StorageByKeyNameTest(unittest.TestCase):
self.assertEqual(None, memcache.get('foo'))
class DecoratorTests(unittest.TestCase):
def setUp(self):
@@ -220,6 +270,10 @@ class DecoratorTests(unittest.TestCase):
client_secret='foo_client_secret',
scope=['foo_scope', 'bar_scope'],
user_agent='foo')
self._finish_setup(decorator, user_mock=UserMock)
def _finish_setup(self, decorator, user_mock):
self.decorator = decorator
class TestRequiredHandler(webapp2.RequestHandler):
@@ -244,7 +298,7 @@ class DecoratorTests(unittest.TestCase):
handler=TestAwareHandler, name='bar')],
debug=True)
self.app = TestApp(application)
users.get_current_user = UserMock
users.get_current_user = user_mock()
self.httplib2_orig = httplib2.Http
httplib2.Http = Http2Mock
@@ -350,6 +404,16 @@ class DecoratorTests(unittest.TestCase):
self.decorator.credentials.access_token)
def test_error_in_step2(self):
# An initial request to an oauth_aware decorated path should not redirect.
response = self.app.get('/bar_path/2012/01')
url = self.decorator.authorize_url()
response = self.app.get('/oauth2callback', {
'error': 'BadStuffHappened'
})
self.assertEqual('200 OK', response.status)
self.assertTrue('BadStuffHappened' in response.body)
def test_kwargs_are_passed_to_underlying_flow(self):
decorator = OAuth2Decorator(client_id='foo_client_id',
client_secret='foo_client_secret',
@@ -362,6 +426,76 @@ class DecoratorTests(unittest.TestCase):
self.assertEqual('foo_user_agent', decorator.flow.user_agent)
self.assertEqual(None, decorator.flow.params.get('user_agent', None))
def test_decorator_from_client_secrets(self):
decorator = oauth2decorator_from_clientsecrets(
datafile('client_secrets.json'),
scope=['foo_scope', 'bar_scope'])
self._finish_setup(decorator, user_mock=UserMock)
self.assertFalse(decorator._in_error)
self.decorator = decorator
self.test_required()
http = self.decorator.http()
self.assertEquals('foo_access_token', http.request.credentials.access_token)
def test_decorator_from_client_secrets_not_logged_in_required(self):
decorator = oauth2decorator_from_clientsecrets(
datafile('client_secrets.json'),
scope=['foo_scope', 'bar_scope'], message='NotLoggedInMessage')
self.decorator = decorator
self._finish_setup(decorator, user_mock=UserNotLoggedInMock)
self.assertFalse(decorator._in_error)
# An initial request to an oauth_required decorated path should be a
# redirect to login.
response = self.app.get('/foo_path')
self.assertTrue(response.status.startswith('302'))
self.assertTrue('Login' in str(response))
def test_decorator_from_client_secrets_not_logged_in_aware(self):
decorator = oauth2decorator_from_clientsecrets(
datafile('client_secrets.json'),
scope=['foo_scope', 'bar_scope'], message='NotLoggedInMessage')
self.decorator = decorator
self._finish_setup(decorator, user_mock=UserNotLoggedInMock)
# An initial request to an oauth_aware decorated path should be a
# redirect to login.
response = self.app.get('/bar_path/2012/03')
self.assertTrue(response.status.startswith('302'))
self.assertTrue('Login' in str(response))
def test_decorator_from_unfilled_client_secrets_required(self):
MESSAGE = 'File is missing'
decorator = oauth2decorator_from_clientsecrets(
datafile('unfilled_client_secrets.json'),
scope=['foo_scope', 'bar_scope'], message=MESSAGE)
self._finish_setup(decorator, user_mock=UserNotLoggedInMock)
self.assertTrue(decorator._in_error)
self.assertEqual(MESSAGE, decorator._message)
# An initial request to an oauth_required decorated path should be an
# error message.
response = self.app.get('/foo_path')
self.assertTrue(response.status.startswith('200'))
self.assertTrue(MESSAGE in str(response))
def test_decorator_from_unfilled_client_secrets_aware(self):
MESSAGE = 'File is missing'
decorator = oauth2decorator_from_clientsecrets(
datafile('unfilled_client_secrets.json'),
scope=['foo_scope', 'bar_scope'], message=MESSAGE)
self._finish_setup(decorator, user_mock=UserNotLoggedInMock)
self.assertTrue(decorator._in_error)
self.assertEqual(MESSAGE, decorator._message)
# An initial request to an oauth_aware decorated path should be an
# error message.
response = self.app.get('/bar_path/2012/03')
self.assertTrue(response.status.startswith('200'))
self.assertTrue(MESSAGE in str(response))
if __name__ == '__main__':
unittest.main()