blob: 964092d90936109a031513cee5735082567f2f70 [file] [log] [blame]
Joe Gregorio432f17e2011-05-22 23:18:00 -04001#!/usr/bin/python2.4
2#
3# Copyright 2010 Google Inc.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17
18"""Discovery document tests
19
20Unit tests for objects created from discovery documents.
21"""
22
23__author__ = 'jcgregorio@google.com (Joe Gregorio)'
24
25import httplib2
26import unittest
27import urlparse
28
29try:
30 from urlparse import parse_qs
31except ImportError:
32 from cgi import parse_qs
33
34from apiclient.http import HttpMockSequence
35from apiclient.anyjson import simplejson
36from webtest import TestApp
37from oauth2client.client import AccessTokenRefreshError
38from oauth2client.client import FlowExchangeError
39from oauth2client.appengine import OAuth2Decorator
40from google.appengine.ext import webapp
41from google.appengine.api import users
42from oauth2client.appengine import OAuth2Handler
43from google.appengine.ext import testbed
44
45
46class UserMock(object):
47 """Mock the app engine user service"""
48 def user_id(self):
49 return 'foo_user'
50
51
52class Http2Mock(object):
53 """Mock httplib2.Http"""
54 status = 200
55 content = {
56 'access_token': 'foo_access_token',
57 'refresh_token': 'foo_refresh_token',
58 'expires_in': 3600
59 }
60
61 def request(self, token_uri, method, body, headers, *args, **kwargs):
62 self.body = body
63 self.headers = headers
64 return (self, simplejson.dumps(self.content))
65
66
67class DecoratorTests(unittest.TestCase):
68
69 def setUp(self):
70 self.testbed = testbed.Testbed()
71 self.testbed.activate()
72 self.testbed.init_datastore_v3_stub()
73 self.testbed.init_memcache_stub()
74 self.testbed.init_user_stub()
75
76 decorator = OAuth2Decorator(client_id='foo_client_id',
77 client_secret='foo_client_secret',
78 scope='foo_scope',
79 user_agent='foo_user_agent')
80 self.decorator = decorator
81
82
83 class TestRequiredHandler(webapp.RequestHandler):
84 @decorator.oauth_required
85 def get(self):
86 pass
87
88
89 class TestAwareHandler(webapp.RequestHandler):
90 @decorator.oauth_aware
91 def get(self):
92 self.response.out.write('Hello World!')
93
94
95 application = webapp.WSGIApplication([('/oauth2callback', OAuth2Handler),
96 ('/foo_path', TestRequiredHandler),
97 ('/bar_path', TestAwareHandler)],
98 debug=True)
99 self.app = TestApp(application)
100 users.get_current_user = UserMock
Joe Gregorio922b78c2011-05-26 21:36:34 -0400101 self.httplib2_orig = httplib2.Http
Joe Gregorio432f17e2011-05-22 23:18:00 -0400102 httplib2.Http = Http2Mock
103
104 def tearDown(self):
105 self.testbed.deactivate()
Joe Gregorio922b78c2011-05-26 21:36:34 -0400106 httplib2.Http = self.httplib2_orig
Joe Gregorio432f17e2011-05-22 23:18:00 -0400107
108 def test_required(self):
109 # An initial request to an oauth_required decorated path should be a
110 # redirect to start the OAuth dance.
111 response = self.app.get('/foo_path')
112 self.assertTrue(response.status.startswith('302'))
113 q = parse_qs(response.headers['Location'].split('?', 1)[1])
114 self.assertEqual('http://localhost/oauth2callback', q['redirect_uri'][0])
115 self.assertEqual('foo_client_id', q['client_id'][0])
116 self.assertEqual('foo_scope', q['scope'][0])
117 self.assertEqual('http://localhost/foo_path', q['state'][0])
118 self.assertEqual('code', q['response_type'][0])
119 self.assertEqual(False, self.decorator.has_credentials())
120
121 # Now simulate the callback to /oauth2callback
122 response = self.app.get('/oauth2callback', {
123 'code': 'foo_access_code',
124 'state': 'foo_path'
125 })
126 self.assertEqual('http://localhost/foo_path', response.headers['Location'])
127 self.assertEqual(None, self.decorator.credentials)
128
129 # Now requesting the decorated path should work
130 response = self.app.get('/foo_path')
131 self.assertEqual('200 OK', response.status)
132 self.assertEqual(True, self.decorator.has_credentials())
133 self.assertEqual('foo_refresh_token', self.decorator.credentials.refresh_token)
134 self.assertEqual('foo_access_token', self.decorator.credentials.access_token)
135
136 # Invalidate the stored Credentials
137 self.decorator.credentials._invalid = True
138 self.decorator.credentials.store(self.decorator.credentials)
139
140 # Invalid Credentials should start the OAuth dance again
141 response = self.app.get('/foo_path')
142 self.assertTrue(response.status.startswith('302'))
143 q = parse_qs(response.headers['Location'].split('?', 1)[1])
144 self.assertEqual('http://localhost/oauth2callback', q['redirect_uri'][0])
145
146 def test_aware(self):
147 # An initial request to an oauth_aware decorated path should not redirect
148 response = self.app.get('/bar_path')
149 self.assertEqual('Hello World!', response.body)
150 self.assertEqual('200 OK', response.status)
151 self.assertEqual(False, self.decorator.has_credentials())
152 url = self.decorator.authorize_url()
153 q = parse_qs(url.split('?', 1)[1])
154 self.assertEqual('http://localhost/oauth2callback', q['redirect_uri'][0])
155 self.assertEqual('foo_client_id', q['client_id'][0])
156 self.assertEqual('foo_scope', q['scope'][0])
157 self.assertEqual('http://localhost/bar_path', q['state'][0])
158 self.assertEqual('code', q['response_type'][0])
159
160 # Now simulate the callback to /oauth2callback
161 url = self.decorator.authorize_url()
162 response = self.app.get('/oauth2callback', {
163 'code': 'foo_access_code',
164 'state': 'bar_path'
165 })
166 self.assertEqual('http://localhost/bar_path', response.headers['Location'])
167 self.assertEqual(False, self.decorator.has_credentials())
168
169 # Now requesting the decorated path will have credentials
170 response = self.app.get('/bar_path')
171 self.assertEqual('200 OK', response.status)
172 self.assertEqual('Hello World!', response.body)
173 self.assertEqual(True, self.decorator.has_credentials())
174 self.assertEqual('foo_refresh_token', self.decorator.credentials.refresh_token)
175 self.assertEqual('foo_access_token', self.decorator.credentials.access_token)
176
177if __name__ == '__main__':
178 unittest.main()