From 23cdd08c02e57449064242ecffc200a1f196ac92 Mon Sep 17 00:00:00 2001 From: MrJmad Date: Tue, 20 Jun 2017 00:32:27 +0200 Subject: [PATCH] Add tests, django 1.9 Support, Tox, flake8 and isort --- .flake8 | 16 ++++++++ .travis.yml | 11 +++++- Makefile | 6 ++- README.rst | 4 +- demo/demoproject/urls.py | 2 +- demo/demoproject/views.py | 2 +- requirements.txt | 2 + setup.py | 2 +- tox.ini | 21 +++++++++++ x509/django/compat.py | 5 +-- x509/django/forms.py | 9 +++-- x509/django/models.py | 12 ++++-- x509/django/utils.py | 6 +-- x509/django/views.py | 3 +- x509/{test_app.py => script_test_app.py} | 3 +- x509/tests/__init__.py | 0 x509/tests/settings_for_tests.py | 23 ++++++++++++ x509/tests/tests_forms.py | 48 ++++++++++++++++++++++++ x509/tests/tests_models.py | 35 +++++++++++++++++ x509/tests/tests_views.py | 32 ++++++++++++++++ 20 files changed, 217 insertions(+), 25 deletions(-) create mode 100644 .flake8 create mode 100644 requirements.txt create mode 100644 tox.ini rename x509/{test_app.py => script_test_app.py} (95%) create mode 100644 x509/tests/__init__.py create mode 100644 x509/tests/settings_for_tests.py create mode 100644 x509/tests/tests_forms.py create mode 100644 x509/tests/tests_models.py create mode 100644 x509/tests/tests_views.py diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..7b14994 --- /dev/null +++ b/.flake8 @@ -0,0 +1,16 @@ +[flake8] +ignore=E501 +max-complexity = 100 +exclude = + .tox, + __pycache__, + .git, + *.css, + *.html, + *.xml, + *.yml, + *.txt, + *.md, + *.js, + .flake8 +filename = *.py diff --git a/.travis.yml b/.travis.yml index 0a972d5..b2bcf99 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,12 @@ language: python python: - "2.7" -install: pip install . -script: python setup.py test +env: + - TOXENV=py27-django18 + - TOXENV=py27-django19 + - TOXENV=isort + - TOXENV=flake8 + +install: + - pip install tox +script: tox -e $TOXENV diff --git a/Makefile b/Makefile index 6d1338a..832113d 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,8 @@ develop: python setup.py develop + +demo: + python setup.py develop (cd demo; python setup.py develop) - demo syncdb + demo migrate + demo runserver diff --git a/README.rst b/README.rst index fbc650f..5c6fc83 100644 --- a/README.rst +++ b/README.rst @@ -196,14 +196,14 @@ Store the certificate in Django class Certificate(models.Model): """Certificate x509 to contact the API.""" - site = models.ForeignKey(Site) + serial = UUIDField(unique=True) dn = models.TextField(_('Distinguished Name')) serial = models.UUIDField(unique=True) created_at = models.DateTimeField() expire_at = models.DateTimeField() def __unicode__(self): - return u'%s - %s' % (self.site, self.dn) + return u'%s' % self.dn Build the certificate diff --git a/demo/demoproject/urls.py b/demo/demoproject/urls.py index 298d4a6..648a8f0 100644 --- a/demo/demoproject/urls.py +++ b/demo/demoproject/urls.py @@ -8,7 +8,7 @@ urlpatterns = patterns( '', - url(r'^/login/$', views.whoami, name='whoami'), + url(r'^login/$', views.whoami, name='whoami'), url(r'^add_pem/$', pem_form_view, name='add_pem'), url(r'^admin/', include(admin.site.urls)), ) diff --git a/demo/demoproject/views.py b/demo/demoproject/views.py index bfdd394..99bff9d 100644 --- a/demo/demoproject/views.py +++ b/demo/demoproject/views.py @@ -6,7 +6,7 @@ class WhoIsLinkedToMe(View): def get(self, request, *args, **kwargs): - response = HttpResponse(mimetype='text/plain') + response = HttpResponse(content_type='text/plain') response.status_code = 403 try: diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..d5eead9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pyOpenSSL +python-dateutil diff --git a/setup.py b/setup.py index 8b38a97..6ec9061 100644 --- a/setup.py +++ b/setup.py @@ -32,5 +32,5 @@ def read_relative_file(filename): packages=['x509'], include_package_data=True, zip_safe=False, - install_requires=['pyOpenSSL'], + install_requires=['pyOpenSSL', 'python-dateutil'], ) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..5d1ae0d --- /dev/null +++ b/tox.ini @@ -0,0 +1,21 @@ +[tox] +envlist = flake8,isort,py27-django{18,19} + +[testenv] +usedevelop = True +deps = + django18: Django>=1.8,<1.9 + django19: Django>=1.9,<1.10 + -rrequirements.txt +commands = + django-admin test --settings=x509.tests.settings_for_tests + +[testenv:flake8] +deps = + flake8 +commands = flake8 x509 + +[testenv:isort] +deps = + isort +commands = isort --recursive --atomic --check-only ./x509/ diff --git a/x509/django/compat.py b/x509/django/compat.py index 7810052..7049c89 100644 --- a/x509/django/compat.py +++ b/x509/django/compat.py @@ -1,10 +1,9 @@ try: - from django.db.models import UUIDField + from django.db.models import UUIDField # noqa F401 except ImportError: # Django < 1.8 try: - from uuidfield import UUIDField + from uuidfield import UUIDField # noqa F401 except ImportError: raise ImportError("In order to use django-x509 with Django < 1.8 you " "must install django-uuidfield.") - diff --git a/x509/django/forms.py b/x509/django/forms.py index def5260..6846e7f 100644 --- a/x509/django/forms.py +++ b/x509/django/forms.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- -from OpenSSL import crypto + from cStringIO import StringIO -from dateutil.parser import parse from uuid import UUID -from django import forms +from dateutil.parser import parse +from OpenSSL import crypto +from django import forms +from x509.django.models import Certificate from x509.exceptions import CertificateAlreadyExist from x509.utils import get_subject_from_components -from x509.django.models import Certificate class PEMForm(forms.Form): diff --git a/x509/django/models.py b/x509/django/models.py index a8b49d2..eee091b 100644 --- a/x509/django/models.py +++ b/x509/django/models.py @@ -1,10 +1,14 @@ # -*- coding: utf-8 -*- + from django.contrib.contenttypes.models import ContentType -from django.contrib.contenttypes import generic from django.db import models - from x509.django.compat import UUIDField +try: + from django.contrib.contenttypes.generic import GenericForeignKey +except ImportError: + from django.contrib.contenttypes.fields import GenericForeignKey + class Certificate(models.Model): """Certificate x509 to link with something.""" @@ -14,7 +18,7 @@ class Certificate(models.Model): expire_at = models.DateTimeField(blank=True, null=True) def __unicode__(self): - return u'%s' % (self.dn) + return u'%s' % self.dn class GenericCertificateM2M(models.Model): @@ -22,7 +26,7 @@ class GenericCertificateM2M(models.Model): certificate = models.ForeignKey(Certificate, related_name='attachees') content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() - content_object = generic.GenericForeignKey('content_type', 'object_id') + content_object = GenericForeignKey('content_type', 'object_id') def __unicode__(self): return u'%s: %s' % (self.content_object, self.certificate) diff --git a/x509/django/utils.py b/x509/django/utils.py index d5742b1..e77b7fc 100644 --- a/x509/django/utils.py +++ b/x509/django/utils.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- from uuid import UUID + from x509.django.models import Certificate -from x509.exceptions import ( - CertificateMissing, CertificateInvalid, HeaderMissing -) +from x509.exceptions import (CertificateInvalid, CertificateMissing, + HeaderMissing) def raise_for_certificate(environ): diff --git a/x509/django/views.py b/x509/django/views.py index 535ca09..9468e35 100644 --- a/x509/django/views.py +++ b/x509/django/views.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- -from django.views.generic import FormView from django.http import HttpResponse - +from django.views.generic import FormView from x509.django.forms import PEMForm diff --git a/x509/test_app.py b/x509/script_test_app.py similarity index 95% rename from x509/test_app.py rename to x509/script_test_app.py index 92b34f6..6a18bbf 100644 --- a/x509/test_app.py +++ b/x509/script_test_app.py @@ -1,6 +1,7 @@ -from flask import Flask, request, make_response from cStringIO import StringIO +from flask import Flask, make_response, request + application = Flask(__name__) diff --git a/x509/tests/__init__.py b/x509/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/x509/tests/settings_for_tests.py b/x509/tests/settings_for_tests.py new file mode 100644 index 0000000..18e21ff --- /dev/null +++ b/x509/tests/settings_for_tests.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': 'django_x509.sqlite', + }, +} + +USE_I18N = True +USE_L10N = True + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'x509.django', +] + +USE_TZ = True + +SECRET_KEY = '0' diff --git a/x509/tests/tests_forms.py b/x509/tests/tests_forms.py new file mode 100644 index 0000000..5a22f57 --- /dev/null +++ b/x509/tests/tests_forms.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- + +import os + +from django.core.files.uploadedfile import SimpleUploadedFile +from django.test import TestCase +from x509.django.forms import PEMForm +from x509.exceptions import CertificateAlreadyExist + +TEST_DIR = os.path.dirname(os.path.abspath(__file__)) +TEST_EXAMPLES_DIR = os.path.join(os.path.dirname(os.path.dirname(TEST_DIR)), "examples") + + +class CertificateFormTestCase(TestCase): + + def test_when_i_post_empty_form_it_is_a_fail(self): + form = PEMForm(data={}) + self.assertFalse(form.is_valid()) + + def test_when_i_post_a_good_form_it_is_valid(self): + certificat_1_path = os.path.join(TEST_EXAMPLES_DIR, 'localhost', 'client1.crt') + + with open(certificat_1_path, 'rb') as cert_file: + form = PEMForm(data={}, files={'pem_file': SimpleUploadedFile('pem_file', cert_file.read())}) + self.assertTrue(form.is_valid()) + + def test_when_i_post_a_good_form_once_i_got_a_certificate(self): + certificat_1_path = os.path.join(TEST_EXAMPLES_DIR, 'localhost', 'client1.crt') + + with open(certificat_1_path, 'rb') as cert_file: + form = PEMForm(data={}, files={'pem_file': SimpleUploadedFile('pem_file', cert_file.read())}) + form.is_valid() + certificate = form.get_certificate() + self.assertEqual(certificate.serial, "34fed2e8613747cb904f935dc17f1aba") + + def test_when_i_post_a_good_form_twice_i_got_an_exception(self): + certificat_1_path = os.path.join(TEST_EXAMPLES_DIR, 'localhost', 'client1.crt') + + with open(certificat_1_path, 'rb') as cert_file: + form = PEMForm(data={}, files={'pem_file': SimpleUploadedFile('pem_file', cert_file.read())}) + form.is_valid() + form.get_certificate() + + with open(certificat_1_path, 'rb') as cert_file: + form = PEMForm(data={}, files={'pem_file': SimpleUploadedFile('pem_file', cert_file.read())}) + form.is_valid() + with self.assertRaises(CertificateAlreadyExist): + form.get_certificate() diff --git a/x509/tests/tests_models.py b/x509/tests/tests_models.py new file mode 100644 index 0000000..2eb4166 --- /dev/null +++ b/x509/tests/tests_models.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +from django.contrib.auth.models import User +from django.contrib.contenttypes.models import ContentType +from django.test import TestCase +from django.utils.timezone import now, timedelta + +from ..django.models import Certificate, GenericCertificateM2M + + +class ModelsTestCase(TestCase): + + def test_i_can_create_a_certificate_instance(self): + created = now() + expire = now() + timedelta(days=320) + test_dn = "My Distinguished Nameeeee" + cert = Certificate.objects.create(serial='{12345678-1234-5678-1234-567812345678}', + dn=test_dn, + created_at=created, + expire_at=expire) + self.assertEqual(cert.created_at, created) + self.assertEqual(cert.expire_at, expire) + self.assertEqual(cert.dn, test_dn) + + def test_i_can_create_a_generic_certificate_m2m(self): + cert = Certificate.objects.create(serial='{12345678-1234-5678-1234-567812345678}', + dn="My Distinguished Nameeeee", + created_at=now(), + expire_at=now() + timedelta(days=320)) + user = User.objects.create(username="user") + certif_link = GenericCertificateM2M.objects.create(certificate=cert, + content_type=ContentType.objects.get_for_model(User), + object_id=user.id) + self.assertEqual(certif_link.content_object, user) + self.assertEqual(certif_link.certificate, cert) diff --git a/x509/tests/tests_views.py b/x509/tests/tests_views.py new file mode 100644 index 0000000..cd724f8 --- /dev/null +++ b/x509/tests/tests_views.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +import os + +from django.test import TestCase +from django.test.client import RequestFactory +from x509.django.models import Certificate +from x509.django.views import PEMFormView +from x509.exceptions import CertificateAlreadyExist + +TEST_DIR = os.path.dirname(os.path.abspath(__file__)) +TEST_EXAMPLES_DIR = os.path.join(os.path.dirname(os.path.dirname(TEST_DIR)), "examples") + + +class PEMFormViewTestCase(TestCase): + def setUp(self): + self.factory = RequestFactory() + + def test_i_can_post_a_certificate(self): + certificat_1_path = os.path.join(TEST_EXAMPLES_DIR, 'localhost', 'client1.crt') + with open(certificat_1_path, 'rb') as cert_file: + request = self.factory.post('/add_pem', {'pem_file': cert_file}) + PEMFormView.as_view()(request) + self.assertEqual(1, Certificate.objects.count()) + + def test_i_cant_post_twice_the_same_certificate(self): + certificat_1_path = os.path.join(TEST_EXAMPLES_DIR, 'localhost', 'client1.crt') + with open(certificat_1_path, 'rb') as cert_file: + request = self.factory.post('/add_pem', {'pem_file': cert_file}) + PEMFormView.as_view()(request) + with self.assertRaises(CertificateAlreadyExist): + PEMFormView.as_view()(request)