Skip to content

Commit 7a27f47

Browse files
author
Jon Wayne Parrott
authored
Add App Engine system tests (#59)
1 parent 421cc9b commit 7a27f47

File tree

8 files changed

+240
-4
lines changed

8 files changed

+240
-4
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ matrix:
1717
- python: 3.5
1818
env: TOXENV=cover
1919
- python: 3.5
20-
env: TOXENV=py35-system SYSTEM_TEST=1
20+
env: TOXENV=py35-system SYSTEM_TEST=1 SKIP_APP_ENGINE_SYSTEM_TEST=1
2121
- python: 2.7
22-
env: TOXENV=py27-system SYSTEM_TEST=1
22+
env: TOXENV=py27-system SYSTEM_TEST=1 SKIP_APP_ENGINE_SYSTEM_TEST=1
2323
cache:
2424
directories:
2525
- ${HOME}/.cache
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
lib
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
api_version: 1
2+
service: google-auth-system-tests
3+
runtime: python27
4+
threadsafe: true
5+
6+
handlers:
7+
- url: .*
8+
script: main.app
9+
10+
libraries:
11+
- name: ssl
12+
version: 2.7.11
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright 2016 Google Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from google.appengine.ext import vendor
16+
17+
# Add any libraries installed in the "lib" folder.
18+
vendor.add('lib')
19+
20+
21+
# Patch os.path.expanduser. This should be fixed in GAE
22+
# versions released after Nov 2016.
23+
import os.path
24+
25+
26+
def patched_expanduser(path):
27+
return path
28+
29+
os.path.expanduser = patched_expanduser
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Copyright 2016 Google Inc. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""App Engine standard application that runs basic system tests for
16+
google.auth.app_engine.
17+
18+
This application has to run tests manually instead of using py.test because
19+
py.test currently doesn't work on App Engine standard.
20+
"""
21+
22+
import contextlib
23+
import json
24+
import sys
25+
from StringIO import StringIO
26+
import traceback
27+
28+
from google.appengine.api import app_identity
29+
import google.auth
30+
from google.auth import _helpers
31+
from google.auth import app_engine
32+
import google.auth.transport.urllib3
33+
import urllib3.contrib.appengine
34+
import webapp2
35+
36+
TOKEN_INFO_URL = 'https://www.googleapis.com/oauth2/v3/tokeninfo'
37+
EMAIL_SCOPE = 'https://www.googleapis.com/auth/userinfo.email'
38+
HTTP = urllib3.contrib.appengine.AppEngineManager()
39+
HTTP_REQUEST = google.auth.transport.urllib3.Request(HTTP)
40+
41+
42+
def test_credentials():
43+
credentials = app_engine.Credentials()
44+
scoped_credentials = credentials.with_scopes([EMAIL_SCOPE])
45+
46+
scoped_credentials.refresh(None)
47+
48+
assert scoped_credentials.valid
49+
assert scoped_credentials.token is not None
50+
51+
# Get token info and verify scope
52+
url = _helpers.update_query(TOKEN_INFO_URL, {
53+
'access_token': scoped_credentials.token,
54+
})
55+
response = HTTP_REQUEST(url=url, method='GET')
56+
token_info = json.loads(response.data.decode('utf-8'))
57+
58+
assert token_info['scope'] == EMAIL_SCOPE
59+
60+
61+
def test_default():
62+
credentials, project_id = google.auth.default()
63+
64+
assert isinstance(credentials, app_engine.Credentials)
65+
assert project_id == app_identity.get_application_id()
66+
67+
68+
@contextlib.contextmanager
69+
def capture():
70+
"""Context manager that captures stderr and stdout."""
71+
oldout, olderr = sys.stdout, sys.stderr
72+
try:
73+
out = StringIO()
74+
sys.stdout, sys.stderr = out, out
75+
yield out
76+
finally:
77+
sys.stdout, sys.stderr = oldout, olderr
78+
79+
80+
def run_test_func(func):
81+
with capture() as capsys:
82+
try:
83+
func()
84+
return True, ''
85+
except Exception as exc:
86+
output = (
87+
'Test {} failed: {}\n\n'
88+
'Stacktrace:\n{}\n\n'
89+
'Captured output:\n{}').format(
90+
func.func_name, exc, traceback.format_exc(),
91+
capsys.getvalue())
92+
return False, output
93+
94+
95+
def run_tests():
96+
"""Runs all tests.
97+
98+
Returns:
99+
Tuple[bool, str]: A tuple containing True if all tests pass, False
100+
otherwise, and any captured output from the tests.
101+
"""
102+
status = True
103+
output = ''
104+
105+
tests = (test_credentials, test_default)
106+
107+
for test in tests:
108+
test_status, test_output = run_test_func(test)
109+
status = status and test_status
110+
output += test_output
111+
112+
return status, output
113+
114+
115+
class MainHandler(webapp2.RequestHandler):
116+
def get(self):
117+
self.response.headers['content-type'] = 'text/plain'
118+
119+
status, output = run_tests()
120+
121+
if not status:
122+
self.response.status = 500
123+
124+
self.response.write(output)
125+
126+
127+
app = webapp2.WSGIApplication([
128+
('/', MainHandler),
129+
], debug=True)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
urllib3
2+
# Relative path to google-auth-python's source.
3+
../../..
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Copyright 2016 Google Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import os
16+
import subprocess
17+
18+
from google.auth import _cloud_sdk
19+
import pytest
20+
21+
SKIP_TEST_ENV = 'SKIP_APP_ENGINE_SYSTEM_TEST'
22+
HERE = os.path.dirname(__file__)
23+
TEST_APP_DIR = os.path.join(HERE, 'app')
24+
TEST_APP_SERVICE = 'google-auth-system-tests'
25+
26+
27+
def vendor_app_dependencies():
28+
"""Vendors in the test application's third-party dependencies."""
29+
subprocess.check_call(
30+
['pip', 'install', '--target', 'lib', '-r', 'requirements.txt'])
31+
32+
33+
def deploy_app():
34+
"""Deploys the test application using gcloud."""
35+
subprocess.check_call(
36+
['gcloud', 'app', 'deploy', '-q', 'app.yaml'])
37+
38+
39+
@pytest.fixture
40+
def app(monkeypatch):
41+
monkeypatch.chdir(TEST_APP_DIR)
42+
43+
vendor_app_dependencies()
44+
deploy_app()
45+
46+
application_id = _cloud_sdk.get_project_id()
47+
application_url = 'https://{}-dot-{}.appspot.com'.format(
48+
TEST_APP_SERVICE, application_id)
49+
50+
yield application_url
51+
52+
53+
@pytest.mark.skipif(
54+
SKIP_TEST_ENV in os.environ,
55+
reason='Explicitly skipping App Engine system tests.')
56+
def test_live_application(app, http_request):
57+
response = http_request(method='GET', url=app)
58+
assert response.status == 200, response.data.decode('utf-8')

tox.ini

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,20 @@ deps =
2424
[testenv:py35-system]
2525
basepython = python3.5
2626
commands =
27-
py.test system_tests
27+
py.test --ignore system_tests/app_engine/app {posargs:system_tests}
2828
deps =
2929
{[testenv]deps}
30+
passenv =
31+
SKIP_APP_ENGINE_SYSTEM_TEST
3032

3133
[testenv:py27-system]
3234
basepython = python2.7
3335
commands =
34-
py.test system_tests
36+
py.test --ignore system_tests/app_engine/app {posargs:system_tests}
3537
deps =
3638
{[testenv]deps}
39+
passenv =
40+
SKIP_APP_ENGINE_SYSTEM_TEST
3741

3842
[testenv:docgen]
3943
basepython = python3.5

0 commit comments

Comments
 (0)