Add App Engine system tests (#59)
diff --git a/.travis.yml b/.travis.yml
index 315f7c1..aa39f9a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -17,9 +17,9 @@
- python: 3.5
env: TOXENV=cover
- python: 3.5
- env: TOXENV=py35-system SYSTEM_TEST=1
+ env: TOXENV=py35-system SYSTEM_TEST=1 SKIP_APP_ENGINE_SYSTEM_TEST=1
- python: 2.7
- env: TOXENV=py27-system SYSTEM_TEST=1
+ env: TOXENV=py27-system SYSTEM_TEST=1 SKIP_APP_ENGINE_SYSTEM_TEST=1
cache:
directories:
- ${HOME}/.cache
diff --git a/system_tests/app_engine/app/.gitignore b/system_tests/app_engine/app/.gitignore
new file mode 100644
index 0000000..a65b417
--- /dev/null
+++ b/system_tests/app_engine/app/.gitignore
@@ -0,0 +1 @@
+lib
diff --git a/system_tests/app_engine/app/app.yaml b/system_tests/app_engine/app/app.yaml
new file mode 100644
index 0000000..872efb3
--- /dev/null
+++ b/system_tests/app_engine/app/app.yaml
@@ -0,0 +1,12 @@
+api_version: 1
+service: google-auth-system-tests
+runtime: python27
+threadsafe: true
+
+handlers:
+- url: .*
+ script: main.app
+
+libraries:
+- name: ssl
+ version: 2.7.11
diff --git a/system_tests/app_engine/app/appengine_config.py b/system_tests/app_engine/app/appengine_config.py
new file mode 100644
index 0000000..da02e10
--- /dev/null
+++ b/system_tests/app_engine/app/appengine_config.py
@@ -0,0 +1,29 @@
+# Copyright 2016 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from google.appengine.ext import vendor
+
+# Add any libraries installed in the "lib" folder.
+vendor.add('lib')
+
+
+# Patch os.path.expanduser. This should be fixed in GAE
+# versions released after Nov 2016.
+import os.path
+
+
+def patched_expanduser(path):
+ return path
+
+os.path.expanduser = patched_expanduser
diff --git a/system_tests/app_engine/app/main.py b/system_tests/app_engine/app/main.py
new file mode 100644
index 0000000..d19567d
--- /dev/null
+++ b/system_tests/app_engine/app/main.py
@@ -0,0 +1,129 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""App Engine standard application that runs basic system tests for
+google.auth.app_engine.
+
+This application has to run tests manually instead of using py.test because
+py.test currently doesn't work on App Engine standard.
+"""
+
+import contextlib
+import json
+import sys
+from StringIO import StringIO
+import traceback
+
+from google.appengine.api import app_identity
+import google.auth
+from google.auth import _helpers
+from google.auth import app_engine
+import google.auth.transport.urllib3
+import urllib3.contrib.appengine
+import webapp2
+
+TOKEN_INFO_URL = 'https://www.googleapis.com/oauth2/v3/tokeninfo'
+EMAIL_SCOPE = 'https://www.googleapis.com/auth/userinfo.email'
+HTTP = urllib3.contrib.appengine.AppEngineManager()
+HTTP_REQUEST = google.auth.transport.urllib3.Request(HTTP)
+
+
+def test_credentials():
+ credentials = app_engine.Credentials()
+ scoped_credentials = credentials.with_scopes([EMAIL_SCOPE])
+
+ scoped_credentials.refresh(None)
+
+ assert scoped_credentials.valid
+ assert scoped_credentials.token is not None
+
+ # Get token info and verify scope
+ url = _helpers.update_query(TOKEN_INFO_URL, {
+ 'access_token': scoped_credentials.token,
+ })
+ response = HTTP_REQUEST(url=url, method='GET')
+ token_info = json.loads(response.data.decode('utf-8'))
+
+ assert token_info['scope'] == EMAIL_SCOPE
+
+
+def test_default():
+ credentials, project_id = google.auth.default()
+
+ assert isinstance(credentials, app_engine.Credentials)
+ assert project_id == app_identity.get_application_id()
+
+
+@contextlib.contextmanager
+def capture():
+ """Context manager that captures stderr and stdout."""
+ oldout, olderr = sys.stdout, sys.stderr
+ try:
+ out = StringIO()
+ sys.stdout, sys.stderr = out, out
+ yield out
+ finally:
+ sys.stdout, sys.stderr = oldout, olderr
+
+
+def run_test_func(func):
+ with capture() as capsys:
+ try:
+ func()
+ return True, ''
+ except Exception as exc:
+ output = (
+ 'Test {} failed: {}\n\n'
+ 'Stacktrace:\n{}\n\n'
+ 'Captured output:\n{}').format(
+ func.func_name, exc, traceback.format_exc(),
+ capsys.getvalue())
+ return False, output
+
+
+def run_tests():
+ """Runs all tests.
+
+ Returns:
+ Tuple[bool, str]: A tuple containing True if all tests pass, False
+ otherwise, and any captured output from the tests.
+ """
+ status = True
+ output = ''
+
+ tests = (test_credentials, test_default)
+
+ for test in tests:
+ test_status, test_output = run_test_func(test)
+ status = status and test_status
+ output += test_output
+
+ return status, output
+
+
+class MainHandler(webapp2.RequestHandler):
+ def get(self):
+ self.response.headers['content-type'] = 'text/plain'
+
+ status, output = run_tests()
+
+ if not status:
+ self.response.status = 500
+
+ self.response.write(output)
+
+
+app = webapp2.WSGIApplication([
+ ('/', MainHandler),
+], debug=True)
diff --git a/system_tests/app_engine/app/requirements.txt b/system_tests/app_engine/app/requirements.txt
new file mode 100644
index 0000000..bd5c476
--- /dev/null
+++ b/system_tests/app_engine/app/requirements.txt
@@ -0,0 +1,3 @@
+urllib3
+# Relative path to google-auth-python's source.
+../../..
diff --git a/system_tests/app_engine/test_app_engine.py b/system_tests/app_engine/test_app_engine.py
new file mode 100644
index 0000000..a6ccb71
--- /dev/null
+++ b/system_tests/app_engine/test_app_engine.py
@@ -0,0 +1,58 @@
+# Copyright 2016 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import subprocess
+
+from google.auth import _cloud_sdk
+import pytest
+
+SKIP_TEST_ENV = 'SKIP_APP_ENGINE_SYSTEM_TEST'
+HERE = os.path.dirname(__file__)
+TEST_APP_DIR = os.path.join(HERE, 'app')
+TEST_APP_SERVICE = 'google-auth-system-tests'
+
+
+def vendor_app_dependencies():
+ """Vendors in the test application's third-party dependencies."""
+ subprocess.check_call(
+ ['pip', 'install', '--target', 'lib', '-r', 'requirements.txt'])
+
+
+def deploy_app():
+ """Deploys the test application using gcloud."""
+ subprocess.check_call(
+ ['gcloud', 'app', 'deploy', '-q', 'app.yaml'])
+
+
+@pytest.fixture
+def app(monkeypatch):
+ monkeypatch.chdir(TEST_APP_DIR)
+
+ vendor_app_dependencies()
+ deploy_app()
+
+ application_id = _cloud_sdk.get_project_id()
+ application_url = 'https://{}-dot-{}.appspot.com'.format(
+ TEST_APP_SERVICE, application_id)
+
+ yield application_url
+
+
+@pytest.mark.skipif(
+ SKIP_TEST_ENV in os.environ,
+ reason='Explicitly skipping App Engine system tests.')
+def test_live_application(app, http_request):
+ response = http_request(method='GET', url=app)
+ assert response.status == 200, response.data.decode('utf-8')
diff --git a/tox.ini b/tox.ini
index 10f7d99..6864dde 100644
--- a/tox.ini
+++ b/tox.ini
@@ -24,16 +24,20 @@
[testenv:py35-system]
basepython = python3.5
commands =
- py.test system_tests
+ py.test --ignore system_tests/app_engine/app {posargs:system_tests}
deps =
{[testenv]deps}
+passenv =
+ SKIP_APP_ENGINE_SYSTEM_TEST
[testenv:py27-system]
basepython = python2.7
commands =
- py.test system_tests
+ py.test --ignore system_tests/app_engine/app {posargs:system_tests}
deps =
{[testenv]deps}
+passenv =
+ SKIP_APP_ENGINE_SYSTEM_TEST
[testenv:docgen]
basepython = python3.5