Merge pull request #319 from p1c2u/feature/django-integration-tests

Django integration tests
This commit is contained in:
A 2021-05-02 20:00:51 +01:00 committed by GitHub
commit b79c49420e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 303 additions and 3 deletions

View file

@ -0,0 +1,27 @@
"""OpenAPI core contrib django backports module"""
class HttpHeaders(dict):
HTTP_PREFIX = 'HTTP_'
# PEP 333 gives two headers which aren't prepended with HTTP_.
UNPREFIXED_HEADERS = {'CONTENT_TYPE', 'CONTENT_LENGTH'}
def __init__(self, environ):
headers = {}
for header, value in environ.items():
name = self.parse_header_name(header)
if name:
headers[name] = value
super(HttpHeaders, self).__init__(headers)
@classmethod
def parse_header_name(cls, header):
if header.startswith(cls.HTTP_PREFIX):
header = header[len(cls.HTTP_PREFIX):]
elif header not in cls.UNPREFIXED_HEADERS:
return None
return header.replace('_', '-').title()
def request_current_scheme_host(req):
return '{}://{}'.format(req.scheme, req.get_host())

View file

@ -0,0 +1,16 @@
"""OpenAPI core contrib django compat module"""
from openapi_core.contrib.django.backports import (
HttpHeaders, request_current_scheme_host,
)
def get_headers(req):
# in Django 1 headers is not defined
return req.headers if hasattr(req, 'headers') else \
HttpHeaders(req.META)
def get_current_scheme_host(req):
# in Django 1 _current_scheme_host is not defined
return req._current_scheme_host if hasattr(req, '_current_scheme_host') \
else request_current_scheme_host(req)

View file

@ -3,6 +3,9 @@ import re
from six.moves.urllib.parse import urljoin from six.moves.urllib.parse import urljoin
from openapi_core.contrib.django.compat import (
get_headers, get_current_scheme_host,
)
from openapi_core.validation.request.datatypes import ( from openapi_core.validation.request.datatypes import (
RequestParameters, OpenAPIRequest, RequestParameters, OpenAPIRequest,
) )
@ -36,14 +39,15 @@ class DjangoOpenAPIRequestFactory(object):
path_pattern = '/' + route path_pattern = '/' + route
path = request.resolver_match and request.resolver_match.kwargs or {} path = request.resolver_match and request.resolver_match.kwargs or {}
headers = get_headers(request)
parameters = RequestParameters( parameters = RequestParameters(
path=path, path=path,
query=request.GET, query=request.GET,
header=request.headers.items(), header=headers.items(),
cookie=request.COOKIES, cookie=request.COOKIES,
) )
full_url_pattern = urljoin( current_scheme_host = get_current_scheme_host(request)
request._current_scheme_host, path_pattern) full_url_pattern = urljoin(current_scheme_host, path_pattern)
return OpenAPIRequest( return OpenAPIRequest(
full_url_pattern=full_url_pattern, full_url_pattern=full_url_pattern,
method=method, method=method,

View file

@ -5,7 +5,9 @@ pytest-cov==2.5.1
falcon==2.0.0; python_version<"3.0" falcon==2.0.0; python_version<"3.0"
falcon==3.0.0; python_version>="3.0" falcon==3.0.0; python_version>="3.0"
flask flask
django==1.11.29; python_version<"3.0"
django==2.2.18; python_version>="3.0" django==2.2.18; python_version>="3.0"
djangorestframework==3.9.4
requests==2.22.0 requests==2.22.0
responses==0.10.12 responses==0.10.12
webob webob

View file

@ -0,0 +1,21 @@
import mock
import pytest
import os
import sys
@pytest.yield_fixture(autouse=True, scope='module')
def django_setup():
directory = os.path.abspath(os.path.dirname(__file__))
django_project_dir = os.path.join(directory, 'data')
sys.path.insert(0, django_project_dir)
with mock.patch.dict(
os.environ,
{
'DJANGO_SETTINGS_MODULE': 'djangoproject.settings',
}
):
import django
django.setup()
yield
sys.path.remove(django_project_dir)

View file

@ -0,0 +1,110 @@
"""
Django settings for djangotest project.
Generated by 'django-admin startproject' using Django 2.2.18.
For more information on this file, see
https://docs.djangoproject.com/en/2.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.2/ref/settings/
"""
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '9=z^yj5yo%g_dyvgdzbceyph^nae)91lq(7^!qqmr1t9wi8b^='
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['testserver']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'djangotest.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'djangotest.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = []
# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/
STATIC_URL = '/static/'
OPENAPI_SPEC_PATH = os.path.join(BASE_DIR, 'openapi.yaml')

View file

@ -0,0 +1,43 @@
import yaml
from django.http import JsonResponse
from openapi_core import create_spec
from openapi_core.validation.request.validators import RequestValidator
from openapi_core.validation.response.validators import ResponseValidator
from openapi_core.contrib.django import (
DjangoOpenAPIRequest, DjangoOpenAPIResponse,
)
from rest_framework.views import APIView
from djangoproject import settings
class TestView(APIView):
def get(self, request, pk):
with open(settings.OPENAPI_SPEC_PATH) as file:
spec_yaml = file.read()
spec_dict = yaml.load(spec_yaml)
spec = create_spec(spec_dict)
openapi_request = DjangoOpenAPIRequest(request)
request_validator = RequestValidator(spec)
result = request_validator.validate(openapi_request)
result.raise_for_errors()
response_dict = {
"test": "test_val",
}
django_response = JsonResponse(response_dict)
openapi_response = DjangoOpenAPIResponse(django_response)
validator = ResponseValidator(spec)
result = validator.validate(openapi_request, openapi_response)
result.raise_for_errors()
return django_response
@staticmethod
def get_extra_actions():
return []

View file

@ -0,0 +1,31 @@
"""djangotest URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import include, path
from djangotest.testapp import views
urlpatterns = [
path('admin/', admin.site.urls),
path(
'api-auth/',
include('rest_framework.urls', namespace='rest_framework'),
),
path(
'test/<int:pk>',
views.TestView.as_view(),
name='test',
),
]

View file

@ -0,0 +1,26 @@
openapi: '3.0.0'
info:
version: '0.0.1'
title: Test Service
paths:
'/test/{pk}':
get:
responses:
'200':
description: Default
content:
application/json:
schema:
type: object
properties:
test:
type: string
required:
- test
parameters:
- required: true
in: path
name: pk
schema:
type: integer
minimum: 1

View file

@ -0,0 +1,20 @@
import pytest
from six import b
class TestDjangoRESTFrameworkAPIView(object):
@pytest.fixture
def api_request_factory(self):
from rest_framework.test import APIRequestFactory
return APIRequestFactory()
def test_get(self, api_request_factory):
from djangoproject.testapp.views import TestView
view = TestView.as_view()
request = api_request_factory.get('/test/4')
response = view(request, pk='4')
assert response.content == b('{"test": "test_val"}')