mirror of
https://github.com/correl/openapi-core.git
synced 2024-11-22 03:00:10 +00:00
Merge pull request #319 from p1c2u/feature/django-integration-tests
Django integration tests
This commit is contained in:
commit
b79c49420e
13 changed files with 303 additions and 3 deletions
27
openapi_core/contrib/django/backports.py
Normal file
27
openapi_core/contrib/django/backports.py
Normal 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())
|
16
openapi_core/contrib/django/compat.py
Normal file
16
openapi_core/contrib/django/compat.py
Normal 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)
|
|
@ -3,6 +3,9 @@ import re
|
|||
|
||||
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 (
|
||||
RequestParameters, OpenAPIRequest,
|
||||
)
|
||||
|
@ -36,14 +39,15 @@ class DjangoOpenAPIRequestFactory(object):
|
|||
path_pattern = '/' + route
|
||||
|
||||
path = request.resolver_match and request.resolver_match.kwargs or {}
|
||||
headers = get_headers(request)
|
||||
parameters = RequestParameters(
|
||||
path=path,
|
||||
query=request.GET,
|
||||
header=request.headers.items(),
|
||||
header=headers.items(),
|
||||
cookie=request.COOKIES,
|
||||
)
|
||||
full_url_pattern = urljoin(
|
||||
request._current_scheme_host, path_pattern)
|
||||
current_scheme_host = get_current_scheme_host(request)
|
||||
full_url_pattern = urljoin(current_scheme_host, path_pattern)
|
||||
return OpenAPIRequest(
|
||||
full_url_pattern=full_url_pattern,
|
||||
method=method,
|
||||
|
|
|
@ -5,7 +5,9 @@ pytest-cov==2.5.1
|
|||
falcon==2.0.0; python_version<"3.0"
|
||||
falcon==3.0.0; python_version>="3.0"
|
||||
flask
|
||||
django==1.11.29; python_version<"3.0"
|
||||
django==2.2.18; python_version>="3.0"
|
||||
djangorestframework==3.9.4
|
||||
requests==2.22.0
|
||||
responses==0.10.12
|
||||
webob
|
||||
|
|
21
tests/integration/contrib/django/conftest.py
Normal file
21
tests/integration/contrib/django/conftest.py
Normal 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)
|
110
tests/integration/contrib/django/data/djangoproject/settings.py
Normal file
110
tests/integration/contrib/django/data/djangoproject/settings.py
Normal 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')
|
|
@ -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 []
|
31
tests/integration/contrib/django/data/djangoproject/urls.py
Normal file
31
tests/integration/contrib/django/data/djangoproject/urls.py
Normal 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',
|
||||
),
|
||||
]
|
26
tests/integration/contrib/django/data/openapi.yaml
Normal file
26
tests/integration/contrib/django/data/openapi.yaml
Normal 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
|
|
@ -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"}')
|
Loading…
Reference in a new issue