Skip to content

Django integration tests #319

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 1 commit into from
May 2, 2021
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
27 changes: 27 additions & 0 deletions openapi_core/contrib/django/backports.py
Original file line number Diff line number Diff line change
@@ -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 changes: 16 additions & 0 deletions openapi_core/contrib/django/compat.py
Original file line number Diff line number Diff line change
@@ -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)
10 changes: 7 additions & 3 deletions openapi_core/contrib/django/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

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,
)
Expand Down Expand Up @@ -36,14 +39,15 @@ def create(cls, request):
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,
Expand Down
2 changes: 2 additions & 0 deletions requirements_dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions tests/integration/contrib/django/conftest.py
Original file line number Diff line number Diff line change
@@ -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)
Empty file.
110 changes: 110 additions & 0 deletions tests/integration/contrib/django/data/djangoproject/settings.py
Original file line number Diff line number Diff line change
@@ -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')
Empty file.
Original file line number Diff line number Diff line change
@@ -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 changes: 31 additions & 0 deletions tests/integration/contrib/django/data/djangoproject/urls.py
Original file line number Diff line number Diff line change
@@ -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 changes: 26 additions & 0 deletions tests/integration/contrib/django/data/openapi.yaml
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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"}')