Skip to content
Snippets Groups Projects
Commit 4699540b authored by Julian's avatar Julian
Browse files

Add basic unit testing and improve CI config

parent 6d7059cf
Branches
Tags
1 merge request!1Add basic unit testing and improve CI config
Pipeline #7769 failed
# 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