Skip to content
Snippets Groups Projects
Commit 7c86f004 authored by Julian's avatar Julian
Browse files

Merge branch 'tests' into 'master'

Add basic unit testing and improve CI config

See merge request uffd/oauth2-proxy!1
parents 6d7059cf 4699540b
No related branches found
No related tags found
No related merge requests found
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
...@@ -10,23 +10,72 @@ before_script: ...@@ -10,23 +10,72 @@ before_script:
- python3 -m pylint --version - python3 -m pylint --version
- python3 -m coverage --version - python3 -m coverage --version
linter: linter:buster:
image: registry.git.cccv.de/uffd/docker-images/buster
stage: test
script:
- pip3 install pylint-gitlab
- python3 -m pylint --exit-zero --rcfile .pylintrc --output-format=pylint_gitlab.GitlabCodeClimateReporter app.py > codeclimate.json
- python3 -m pylint --exit-zero --rcfile .pylintrc --output-format=pylint_gitlab.GitlabPagesHtmlReporter app.py > pylint.html
- python3 -m pylint --rcfile .pylintrc --output-format=text app.py
artifacts:
when: always
paths:
- pylint.html
reports:
codequality: codeclimate.json
linter:bullseye:
image: registry.git.cccv.de/uffd/docker-images/bullseye
stage: test
script:
- pip3 install pylint-gitlab
- python3 -m pylint --exit-zero --rcfile .pylintrc --output-format=pylint_gitlab.GitlabCodeClimateReporter app.py > codeclimate.json
- python3 -m pylint --exit-zero --rcfile .pylintrc --output-format=pylint_gitlab.GitlabPagesHtmlReporter app.py > pylint.html
- python3 -m pylint --rcfile .pylintrc --output-format=text app.py
artifacts:
when: always
paths:
- pylint.html
reports:
codequality: codeclimate.json
unittests:buster:
image: registry.git.cccv.de/uffd/docker-images/buster
stage: test stage: test
script: script:
- python3 -m pylint --rcfile .pylintrc --output-format=text app.py | tee pylint.txt - service slapd start
- UNITTEST_OPENLDAP=1 python3-coverage run --include 'app.py' -m pytest --junitxml=report.xml || true
- python3-coverage report -m
- python3-coverage html
- python3-coverage xml
artifacts: artifacts:
when: always
paths: paths:
- pylint.txt - htmlcov/index.html
- htmlcov
expose_as: 'Coverage Report'
reports:
cobertura: coverage.xml
junit: report.xml
coverage: '/^TOTAL.*\s+(\d+\%)$/'
#unittest: unittests:bullseye:
# stage: test image: registry.git.cccv.de/uffd/docker-images/bullseye
# script: stage: test
# - python3 -m coverage run runTests.py script:
# - python3 -m coverage report --include "./*" - service slapd start
# - python3 -m coverage report -m --include "./*" > report.txt - UNITTEST_OPENLDAP=1 python3-coverage run --include 'app.py' -m pytest --junitxml=report.xml || true
# - python3 -m coverage html --include "./*" #- python3-coverage report -m
# artifacts: - python3-coverage html
# paths: #- python3-coverage xml
# - htmlcov/* artifacts:
# - .coverage when: always
# - report.txt paths:
- htmlcov/index.html
- htmlcov
expose_as: 'Coverage Report'
reports:
#cobertura: coverage.xml
junit: report.xml
#coverage: '/^TOTAL.*\s+(\d+\%)$/'
...@@ -63,90 +63,6 @@ confidence= ...@@ -63,90 +63,6 @@ confidence=
disable=missing-module-docstring, disable=missing-module-docstring,
missing-class-docstring, missing-class-docstring,
missing-function-docstring, missing-function-docstring,
print-statement,
parameter-unpacking,
unpacking-in-except,
old-raise-syntax,
backtick,
long-suffix,
old-ne-operator,
old-octal-literal,
import-star-module-level,
non-ascii-bytes-literal,
raw-checker-failed,
bad-inline-option,
locally-disabled,
file-ignored,
suppressed-message,
useless-suppression,
deprecated-pragma,
use-symbolic-message-instead,
unused-wildcard-import,
apply-builtin,
basestring-builtin,
buffer-builtin,
cmp-builtin,
coerce-builtin,
execfile-builtin,
file-builtin,
long-builtin,
raw_input-builtin,
reduce-builtin,
standarderror-builtin,
unicode-builtin,
xrange-builtin,
coerce-method,
delslice-method,
getslice-method,
setslice-method,
no-absolute-import,
old-division,
dict-iter-method,
dict-view-method,
next-method-called,
metaclass-assignment,
indexing-exception,
raising-string,
reload-builtin,
oct-method,
hex-method,
nonzero-method,
cmp-method,
input-builtin,
round-builtin,
intern-builtin,
unichr-builtin,
map-builtin-not-iterating,
zip-builtin-not-iterating,
range-builtin-not-iterating,
filter-builtin-not-iterating,
using-cmp-argument,
eq-without-hash,
div-method,
idiv-method,
rdiv-method,
exception-message-attribute,
invalid-str-codec,
sys-max-int,
bad-python3-import,
deprecated-string-function,
deprecated-str-translate-call,
deprecated-itertools-function,
deprecated-types-field,
next-method-defined,
dict-items-not-iterating,
dict-keys-not-iterating,
dict-values-not-iterating,
deprecated-operator-function,
deprecated-urllib-function,
xreadlines-attribute,
deprecated-sys-function,
exception-escape,
comprehension-escape,
too-few-public-methods,
method-hidden,
bad-continuation,
unused-variable,
# Enable the message, report, category or checker with the given id(s). You can # Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option # either give multiple identifier separated by comma (,) or put this option
......
# OAuthProxy will usually served from the same domain as the services that # OAuthProxy is usually served from the same domain as the service that
# use it for OAuth integration, so make sure that the session cookie does # uses it for OAuth2 integration. Make sure that the session cookie does
# not conflict with any other cookies! # not conflict with any other cookies!
SESSION_COOKIE_NAME = 'oauth-session' SESSION_COOKIE_NAME = 'oauth-session'
......
import unittest
try:
import mock
except ImportError:
from unittest import mock
import json
import urllib.parse
from flask import session
from requests import Session, Response
from app import create_app
headers = {
'X-CLIENT-ID': 'test_client_id',
'X-CLIENT-SECRET': 'test_client_secret',
'X-REDIRECT-URI': 'https://127.0.0.123:7654/callback',
}
class MockRequest:
def __init__(self):
self.headers = []
self.body = ''
class MockResponse:
def __init__(self, status_code, json_data=None):
self.request = MockRequest()
self.ok = status_code == 200
self.status_code = status_code
self.json = lambda: json_data
self.headers = []
self.text = json.dumps(json_data)
def mock_request(self, method, url, **kwargs):
if method == 'POST' and url == 'https://127.0.0.123:4567/token':
return MockResponse(200, {'access_token': '2YotnFZFEjr1zCsicMWpAA',
'token_type': 'Bearer',
'expires_in': 3600,
'refresh_token': 'tGzv3JOkF0XG5Qx2TlKWIA'})
if method == 'GET' and url == 'https://127.0.0.123:4567/userinfo':
if kwargs['headers']['Authorization'] != 'Bearer 2YotnFZFEjr1zCsicMWpAA':
raise Exception()
return MockResponse(200, {'id': 1234,
'name': 'Test User',
'nickname': 'testuser',
'email': 'test@example.com',
'ldap_dn': 'uid=testuser,ou=users,dc=example,dc=com',
'groups': ['uffd_access', 'users']})
print(repr(method), repr(url), repr(kwargs))
raise Exception()
@mock.patch.object(Session, 'request', new=mock_request)
class TestCases(unittest.TestCase):
def setUp(self):
config = {
'TESTING': True,
'DEBUG': True,
'SECRET_KEY': 'DEBUGKEY',
'OAUTH2_AUTH_URL': 'https://127.0.0.123:4567/authorize',
'OAUTH2_TOKEN_URL': 'https://127.0.0.123:4567/token',
'OAUTH2_USERINFO_URL': 'https://127.0.0.123:4567/userinfo',
}
self.app = create_app(config)
self.client = self.app.test_client()
self.client.__enter__()
# Just do some request so that we can use url_for
self.client.get(path='/')
def tearDown(self):
self.client.__exit__(None, None, None)
def test_status(self):
r = self.client.get(path='/status', headers=headers)
self.assertEqual(r.status_code, 200)
self.assertIn('test_client_id', r.data.decode())
self.assertNotIn('test_client_secret', r.data.decode())
def test_auth_no_session(self):
r = self.client.get(path='/auth', headers=headers)
self.assertEqual(r.status_code, 401)
def test_login(self):
r = self.client.get(path='/login', query_string={'url': 'https://127.0.0.123:7654/app'}, headers=headers, follow_redirects=False)
self.assertEqual(r.status_code, 302)
url = urllib.parse.urlparse(r.location)
qs = urllib.parse.parse_qs(url.query)
self.assertEqual(url.scheme, 'https')
self.assertEqual(url.netloc, '127.0.0.123:4567')
self.assertEqual(url.path, '/authorize')
self.assertEqual(qs['response_type'], ['code'])
self.assertEqual(qs['client_id'], ['test_client_id'])
self.assertEqual(qs['redirect_uri'], ['https://127.0.0.123:7654/callback'])
self.assertGreater(len(qs['state'][0]), 8)
self.assertEqual(session['state'], qs['state'][0])
self.assertEqual(session['url'], 'https://127.0.0.123:7654/app')
def test_callback(self):
code = 'testcode'
state = 'teststate'
with self.client.session_transaction() as session:
session['state'] = state
session['url'] = 'https://127.0.0.123:7654/app'
r = self.client.get(path='/callback', headers=headers, query_string={'code': code, 'state': state}, follow_redirects=False)
self.assertEqual(r.status_code, 302)
self.assertEqual(r.location, 'https://127.0.0.123:7654/app')
with self.client.session_transaction() as session:
self.assertEqual(session['user_id'], 1234)
self.assertEqual(session['user_name'], 'Test User')
self.assertEqual(session['user_nickname'], 'testuser')
self.assertEqual(session['user_email'], 'test@example.com')
self.assertEqual(session['user_ldap_dn'], 'uid=testuser,ou=users,dc=example,dc=com')
self.assertEqual(set(session['user_groups']), set(['uffd_access', 'users']))
self.assertNotIn('state', session)
self.assertNotIn('url', session)
def test_auth_session(self):
with self.client.session_transaction() as session:
session['user_id'] = 1234
session['user_name'] = 'Test User'
session['user_nickname'] = 'testuser'
session['user_email'] = 'test@example.com'
session['user_ldap_dn'] = 'uid=testuser,ou=users,dc=example,dc=com'
session['user_groups'] = ['uffd_access', 'users']
r = self.client.get(path='/auth', headers=headers)
self.assertEqual(r.status_code, 200)
self.assertEqual(r.headers['OAUTH-USER-ID'], '1234')
self.assertEqual(r.headers['OAUTH-USER-NAME'], 'Test User')
self.assertEqual(r.headers['OAUTH-USER-NICKNAME'], 'testuser')
self.assertEqual(r.headers['OAUTH-USER-EMAIL'], 'test@example.com')
self.assertEqual(r.headers['OAUTH-USER-LDAP-DN'], 'uid=testuser,ou=users,dc=example,dc=com')
self.assertIn(r.headers['OAUTH-USER-GROUPS'], ['uffd_access,users', 'users,uffd_access'])
def test_logout(self):
with self.client.session_transaction() as session:
session['user_id'] = 1234
session['user_name'] = 'Test User'
session['user_nickname'] = 'testuser'
session['user_email'] = 'test@example.com'
session['user_ldap_dn'] = 'uid=testuser,ou=users,dc=example,dc=com'
session['user_groups'] = ['uffd_access', 'users']
r = self.client.get(path='/logout', headers=headers)
self.assertEqual(r.status_code, 200)
with self.client.session_transaction() as session:
self.assertEqual(list(session.keys()), [])
def test_logout_no_session(self):
r = self.client.get(path='/logout', headers=headers)
self.assertEqual(r.status_code, 200)
with self.client.session_transaction() as session:
self.assertEqual(list(session.keys()), [])
def test_logout_redirect(self):
with self.client.session_transaction() as session:
session['user_id'] = 1234
session['user_name'] = 'Test User'
session['user_nickname'] = 'testuser'
session['user_email'] = 'test@example.com'
session['user_ldap_dn'] = 'uid=testuser,ou=users,dc=example,dc=com'
session['user_groups'] = ['uffd_access', 'users']
r = self.client.get(path='/logout', headers=headers, query_string={'redirect_url': 'https://127.0.0.123:7654/app/logout'})
self.assertEqual(r.status_code, 302)
self.assertEqual(r.location, 'https://127.0.0.123:7654/app/logout')
with self.client.session_transaction() as session:
self.assertEqual(list(session.keys()), [])
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment