Skip to content

Setup tests #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Dec 17, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
.coverage
covhtml/

*.pyc
__pycache__/

Expand Down
12 changes: 7 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@ language: python

python:
- "2.7"
- "3.3"
- "3.4"
- "3.5"
- "pypy"
- "pypy3"

env:
- DJANGO_VERSION=1.8
- DJANGO_VERSION=1.9

install:
- pip install -r requirements.txt
- pip install -r requirements.txt
- pip install -U Django==$DJANGO_VERSION

script:
- python runtests.py
- python runtests.py

notifications:
slack:
Expand Down
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
Django==1.8.7
djangorestframework==3.3.2
coverage==4.0.3
flake8==2.5.1
15 changes: 10 additions & 5 deletions rest_framework_docs/api_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def __init__(self, pattern, parent_pattern=None):
self.path = self.__get_path__(parent_pattern)
self.allowed_methods = self.__get_allowed_methods__()
# self.view_name = pattern.callback.__name__
self.errors = None
self.fields = self.__get_serializer_fields__()

def __get_path__(self, parent_pattern):
Expand All @@ -27,11 +28,15 @@ def __get_serializer_fields__(self):
if hasattr(self.callback.cls, 'serializer_class') and hasattr(self.callback.cls.serializer_class, 'get_fields'):
serializer = self.callback.cls.serializer_class
if hasattr(serializer, 'get_fields'):
fields = [{
"name": key,
"type": str(field.__class__.__name__),
"required": field.required
} for key, field in serializer().get_fields().items()]
try:
fields = [{
"name": key,
"type": str(field.__class__.__name__),
"required": field.required
} for key, field in serializer().get_fields().items()]
except KeyError as e:
self.errors = e
fields = []

# FIXME:
# Show more attibutes of `field`?
Expand Down
5 changes: 5 additions & 0 deletions rest_framework_docs/static/less/style.less
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ body {

.panel-body {
padding: 0;

.alert {
padding: 5px 10px;
margin-top: 10px;
}
}

> .panel-heading +.panel-collapse > .panel-body { border: 0; }
Expand Down
10 changes: 7 additions & 3 deletions rest_framework_docs/templates/rest_framework_docs/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Jump To <span class="caret"></span></a>
<ul class="dropdown-menu">
{% for group in endpoints_grouped %}
<li><a href="#{{ group.grouper|lower }}-nav">{{ group.grouper }}</a></li>
<li><a href="#{{ group.grouper|lower }}-group">{{ group.grouper }}</a></li>
{% endfor %}
</ul>
</li>
Expand All @@ -20,7 +20,7 @@
{% if endpoints_grouped %}
{% for group in endpoints_grouped %}

<h1 id="{{ group.grouper|lower }}-nav">{{group.grouper}}</h1>
<h1 id="{{ group.grouper|lower }}-group">{{group.grouper}}</h1>

<div class="panel-group" role="tablist">

Expand Down Expand Up @@ -48,14 +48,18 @@ <h4 class="panel-title title">

<div id="{{ endpoint.path|slugify }}" class="panel-collapse collapse" role="tabpanel">
<div class="panel-body">
{% if endpoint.errors %}
<div class="alert alert-danger" role="alert">Oops! There was something wrong with {{ endpoint.errors }}. Please check your code.</div>
{% endif %}

{% if endpoint.fields %}
<p>Fields:</p>
<ul class="list fields">
{% for field in endpoint.fields %}
<li class="field">{{ field.name }}: {{ field.type }} {% if field.required %}<span class="label label-primary label-required" title="Required">R</span>{% endif %}</li>
{% endfor %}
</ul>
{% else %}
{% elif not endpoint.errors %}
<p>No fields.</p>
{% endif %}
</div>
Expand Down
32 changes: 31 additions & 1 deletion runtests.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
#!/usr/bin/env python
import os
import sys
import subprocess
import django
from coverage import coverage
from django.conf import settings
from django.test.utils import get_runner


FLAKE8_ARGS = ['demo/project/', 'rest_framework_docs', '--ignore=E501']
FLAKE8_ARGS = ['demo/project/', 'rest_framework_docs', 'tests/', '--ignore=E501']


def exit_on_failure(command, message=None):
Expand All @@ -17,4 +22,29 @@ def flake8_main(args):
print("" if command else "Success. flake8 passed.")
return command


def run_tests_coverage():
if __name__ == "__main__":
os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.settings'
django.setup()
TestRunner = get_runner(settings)
test_runner = TestRunner()

# Setup Coverage
cov = coverage(source=["rest_framework_docs"], omit=["rest_framework_docs/__init__.py"])
cov.start()

failures = test_runner.run_tests(["tests"])

if bool(failures):
cov.erase()
sys.exit("Tests Failed. Coverage Cancelled.")

# If success show coverage results
cov.stop()
cov.save()
cov.report()
cov.html_report(directory='covhtml')

exit_on_failure(flake8_main(FLAKE8_ARGS))
exit_on_failure(run_tests_coverage())
Empty file added tests/__init__.py
Empty file.
49 changes: 49 additions & 0 deletions tests/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import uuid
from django.db import models
from django.contrib.auth.models import AbstractBaseUser


class User(AbstractBaseUser):
created = models.DateTimeField(auto_now_add=True, db_index=True)
modified = models.DateTimeField(auto_now=True)

email = models.EmailField(unique=True, verbose_name='email address', max_length=255)
full_name = models.CharField(max_length=255)

is_active = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)

USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['email', 'full_name']


class Organisation(models.Model):

id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)

name = models.CharField(unique=True, max_length=100)
slug = models.SlugField(unique=True, null=True, blank=True)
members = models.ManyToManyField(User, through='Membership', through_fields=('organisation', 'user'))

is_active = models.BooleanField(default=False)


class Membership(models.Model):

class Meta:
unique_together = ("organisation", "user")

MEMBER_ROLES = (
("ADMIN", "Admin"),
("USER", "User")
)

id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
joined = models.DateTimeField(auto_now_add=True)

organisation = models.ForeignKey(Organisation)
user = models.ForeignKey(User)
role = models.CharField(choices=MEMBER_ROLES, max_length=20, default="USER")
is_owner = models.BooleanField(default=False)
66 changes: 66 additions & 0 deletions tests/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from __future__ import absolute_import, division, print_function

from rest_framework import serializers
from tests.models import User, Organisation, Membership


class UserRegistrationSerializer(serializers.ModelSerializer):

class Meta:
model = User
fields = ('email', 'full_name', 'password',)
extra_kwargs = {'password': {'write_only': True}}


class UserProfileSerializer(serializers.ModelSerializer):

class Meta:
model = User
fields = ('email', 'full_name', 'password', 'is_active')
extra_kwargs = {
'password': {'write_only': True}
}
read_only_fields = ('is_active',)


class ResetPasswordSerializer(serializers.ModelSerializer):

id = serializers.CharField()
token = serializers.CharField()

class Meta:
model = User
fields = ('id', 'token', 'password',)
extra_kwargs = {'password': {'write_only': True}}


class CreateOrganisationSerializer(serializers.ModelSerializer):

class Meta:
model = Organisation
fields = ('name', 'slug',)


class OrganisationMembersSerializer(serializers.ModelSerializer):
user = serializers.SerializerMethodField()

class Meta:
model = Membership
fields = ('joined', 'user', 'is_owner', 'role')

def get_user(self, obj):
serializer = UserProfileSerializer(obj.user)
return serializer.data


class OrganisationErroredSerializer(serializers.ModelSerializer):

class Meta:
model = Organisation
fields = ('name', 'slug', 'is_active')

def __init__(self, *args, **kwargs):
super(OrganisationErroredSerializer, self).__init__(*args, **kwargs)

# Should raise a KeyError
self.context["test_value"]
41 changes: 41 additions & 0 deletions tests/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import os

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

SECRET_KEY = 'django-rest-framework-docs-key'

# Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}

INSTALLED_APPS = [
# Django Apps
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.staticfiles',

# External Packages
"rest_framework",
"rest_framework_docs",

# Test apps
"tests"
]

ROOT_URLCONF = 'tests.urls'

REST_FRAMEWORK_DOCS = {
'HIDDEN': False
}

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/

STATIC_URL = '/static/'
52 changes: 52 additions & 0 deletions tests/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from django.core.urlresolvers import reverse
from django.test import TestCase, override_settings
from rest_framework_docs.settings import DRFSettings


class DRFDocsViewTests(TestCase):

SETTINGS_HIDE_DOCS = {
'HIDDEN': True # Default: False
}

def setUp(self):
super(DRFDocsViewTests, self).setUp()

def test_settings_module(self):

settings = DRFSettings()

self.assertEqual(settings.get_setting("HIDDEN"), False)
self.assertEqual(settings.get_setting("TEST"), None)

def test_index_view_with_endpoints(self):
"""
Should load the drf focs view with all the endpoints.
NOTE: Views that do **not** inherit from DRF's "APIView" are not included.
"""
response = self.client.get(reverse('drfdocs'))

self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context["endpoints"]), 10)

# Test the login view
self.assertEqual(response.context["endpoints"][0].name_parent, "accounts")
self.assertEqual(response.context["endpoints"][0].allowed_methods, ['POST', 'OPTIONS'])
self.assertEqual(response.context["endpoints"][0].path, "/accounts/login/")
self.assertEqual(len(response.context["endpoints"][0].fields), 2)
self.assertEqual(response.context["endpoints"][0].fields[0]["type"], "CharField")
self.assertTrue(response.context["endpoints"][0].fields[0]["required"])

# The view "OrganisationErroredView" (organisations/(?P<slug>[\w-]+)/errored/) should contain an error.
self.assertEqual(str(response.context["endpoints"][8].errors), "'test_value'")

@override_settings(REST_FRAMEWORK_DOCS=SETTINGS_HIDE_DOCS)
def test_index_view_docs_hidden(self):
"""
Should prevent the docs from loading the "HIDDEN" is set
to "False" in settings
"""
response = self.client.get(reverse('drfdocs'))

self.assertEqual(response.status_code, 404)
self.assertEqual(response.reason_phrase.upper(), "NOT FOUND")
Loading