Skip to content

Commit b79c494

Browse files
authored
Merge pull request #319 from p1c2u/feature/django-integration-tests
Django integration tests
2 parents a65ce7a + 8773b6c commit b79c494

File tree

13 files changed

+303
-3
lines changed

13 files changed

+303
-3
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"""OpenAPI core contrib django backports module"""
2+
3+
4+
class HttpHeaders(dict):
5+
HTTP_PREFIX = 'HTTP_'
6+
# PEP 333 gives two headers which aren't prepended with HTTP_.
7+
UNPREFIXED_HEADERS = {'CONTENT_TYPE', 'CONTENT_LENGTH'}
8+
9+
def __init__(self, environ):
10+
headers = {}
11+
for header, value in environ.items():
12+
name = self.parse_header_name(header)
13+
if name:
14+
headers[name] = value
15+
super(HttpHeaders, self).__init__(headers)
16+
17+
@classmethod
18+
def parse_header_name(cls, header):
19+
if header.startswith(cls.HTTP_PREFIX):
20+
header = header[len(cls.HTTP_PREFIX):]
21+
elif header not in cls.UNPREFIXED_HEADERS:
22+
return None
23+
return header.replace('_', '-').title()
24+
25+
26+
def request_current_scheme_host(req):
27+
return '{}://{}'.format(req.scheme, req.get_host())

openapi_core/contrib/django/compat.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""OpenAPI core contrib django compat module"""
2+
from openapi_core.contrib.django.backports import (
3+
HttpHeaders, request_current_scheme_host,
4+
)
5+
6+
7+
def get_headers(req):
8+
# in Django 1 headers is not defined
9+
return req.headers if hasattr(req, 'headers') else \
10+
HttpHeaders(req.META)
11+
12+
13+
def get_current_scheme_host(req):
14+
# in Django 1 _current_scheme_host is not defined
15+
return req._current_scheme_host if hasattr(req, '_current_scheme_host') \
16+
else request_current_scheme_host(req)

openapi_core/contrib/django/requests.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
from six.moves.urllib.parse import urljoin
55

6+
from openapi_core.contrib.django.compat import (
7+
get_headers, get_current_scheme_host,
8+
)
69
from openapi_core.validation.request.datatypes import (
710
RequestParameters, OpenAPIRequest,
811
)
@@ -36,14 +39,15 @@ def create(cls, request):
3639
path_pattern = '/' + route
3740

3841
path = request.resolver_match and request.resolver_match.kwargs or {}
42+
headers = get_headers(request)
3943
parameters = RequestParameters(
4044
path=path,
4145
query=request.GET,
42-
header=request.headers.items(),
46+
header=headers.items(),
4347
cookie=request.COOKIES,
4448
)
45-
full_url_pattern = urljoin(
46-
request._current_scheme_host, path_pattern)
49+
current_scheme_host = get_current_scheme_host(request)
50+
full_url_pattern = urljoin(current_scheme_host, path_pattern)
4751
return OpenAPIRequest(
4852
full_url_pattern=full_url_pattern,
4953
method=method,

requirements_dev.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ pytest-cov==2.5.1
55
falcon==2.0.0; python_version<"3.0"
66
falcon==3.0.0; python_version>="3.0"
77
flask
8+
django==1.11.29; python_version<"3.0"
89
django==2.2.18; python_version>="3.0"
10+
djangorestframework==3.9.4
911
requests==2.22.0
1012
responses==0.10.12
1113
webob
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import mock
2+
import pytest
3+
import os
4+
import sys
5+
6+
7+
@pytest.yield_fixture(autouse=True, scope='module')
8+
def django_setup():
9+
directory = os.path.abspath(os.path.dirname(__file__))
10+
django_project_dir = os.path.join(directory, 'data')
11+
sys.path.insert(0, django_project_dir)
12+
with mock.patch.dict(
13+
os.environ,
14+
{
15+
'DJANGO_SETTINGS_MODULE': 'djangoproject.settings',
16+
}
17+
):
18+
import django
19+
django.setup()
20+
yield
21+
sys.path.remove(django_project_dir)

tests/integration/contrib/django/data/djangoproject/__init__.py

Whitespace-only changes.
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
"""
2+
Django settings for djangotest project.
3+
4+
Generated by 'django-admin startproject' using Django 2.2.18.
5+
6+
For more information on this file, see
7+
https://docs.djangoproject.com/en/2.2/topics/settings/
8+
9+
For the full list of settings and their values, see
10+
https://docs.djangoproject.com/en/2.2/ref/settings/
11+
"""
12+
13+
import os
14+
15+
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
16+
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17+
18+
19+
# Quick-start development settings - unsuitable for production
20+
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
21+
22+
# SECURITY WARNING: keep the secret key used in production secret!
23+
SECRET_KEY = '9=z^yj5yo%g_dyvgdzbceyph^nae)91lq(7^!qqmr1t9wi8b^='
24+
25+
# SECURITY WARNING: don't run with debug turned on in production!
26+
DEBUG = True
27+
28+
ALLOWED_HOSTS = ['testserver']
29+
30+
31+
# Application definition
32+
33+
INSTALLED_APPS = [
34+
'django.contrib.admin',
35+
'django.contrib.auth',
36+
'django.contrib.contenttypes',
37+
'django.contrib.sessions',
38+
'django.contrib.messages',
39+
'django.contrib.staticfiles',
40+
'rest_framework',
41+
]
42+
43+
MIDDLEWARE = [
44+
'django.middleware.security.SecurityMiddleware',
45+
'django.contrib.sessions.middleware.SessionMiddleware',
46+
'django.middleware.common.CommonMiddleware',
47+
'django.middleware.csrf.CsrfViewMiddleware',
48+
'django.contrib.auth.middleware.AuthenticationMiddleware',
49+
'django.contrib.messages.middleware.MessageMiddleware',
50+
'django.middleware.clickjacking.XFrameOptionsMiddleware',
51+
]
52+
53+
ROOT_URLCONF = 'djangotest.urls'
54+
55+
TEMPLATES = [
56+
{
57+
'BACKEND': 'django.template.backends.django.DjangoTemplates',
58+
'DIRS': [],
59+
'APP_DIRS': True,
60+
'OPTIONS': {
61+
'context_processors': [
62+
'django.template.context_processors.debug',
63+
'django.template.context_processors.request',
64+
'django.contrib.auth.context_processors.auth',
65+
'django.contrib.messages.context_processors.messages',
66+
],
67+
},
68+
},
69+
]
70+
71+
WSGI_APPLICATION = 'djangotest.wsgi.application'
72+
73+
74+
# Database
75+
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
76+
77+
DATABASES = {
78+
'default': {
79+
'ENGINE': 'django.db.backends.sqlite3',
80+
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
81+
}
82+
}
83+
84+
85+
# Password validation
86+
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
87+
88+
AUTH_PASSWORD_VALIDATORS = []
89+
90+
91+
# Internationalization
92+
# https://docs.djangoproject.com/en/2.2/topics/i18n/
93+
94+
LANGUAGE_CODE = 'en-us'
95+
96+
TIME_ZONE = 'UTC'
97+
98+
USE_I18N = True
99+
100+
USE_L10N = True
101+
102+
USE_TZ = True
103+
104+
105+
# Static files (CSS, JavaScript, Images)
106+
# https://docs.djangoproject.com/en/2.2/howto/static-files/
107+
108+
STATIC_URL = '/static/'
109+
110+
OPENAPI_SPEC_PATH = os.path.join(BASE_DIR, 'openapi.yaml')

tests/integration/contrib/django/data/djangoproject/testapp/__init__.py

Whitespace-only changes.

tests/integration/contrib/django/data/djangoproject/testapp/migrations/__init__.py

Whitespace-only changes.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import yaml
2+
3+
from django.http import JsonResponse
4+
from openapi_core import create_spec
5+
from openapi_core.validation.request.validators import RequestValidator
6+
from openapi_core.validation.response.validators import ResponseValidator
7+
from openapi_core.contrib.django import (
8+
DjangoOpenAPIRequest, DjangoOpenAPIResponse,
9+
)
10+
from rest_framework.views import APIView
11+
12+
from djangoproject import settings
13+
14+
15+
class TestView(APIView):
16+
17+
def get(self, request, pk):
18+
with open(settings.OPENAPI_SPEC_PATH) as file:
19+
spec_yaml = file.read()
20+
spec_dict = yaml.load(spec_yaml)
21+
spec = create_spec(spec_dict)
22+
23+
openapi_request = DjangoOpenAPIRequest(request)
24+
25+
request_validator = RequestValidator(spec)
26+
result = request_validator.validate(openapi_request)
27+
result.raise_for_errors()
28+
29+
response_dict = {
30+
"test": "test_val",
31+
}
32+
django_response = JsonResponse(response_dict)
33+
34+
openapi_response = DjangoOpenAPIResponse(django_response)
35+
validator = ResponseValidator(spec)
36+
result = validator.validate(openapi_request, openapi_response)
37+
result.raise_for_errors()
38+
39+
return django_response
40+
41+
@staticmethod
42+
def get_extra_actions():
43+
return []
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""djangotest URL Configuration
2+
3+
The `urlpatterns` list routes URLs to views. For more information please see:
4+
https://docs.djangoproject.com/en/2.2/topics/http/urls/
5+
Examples:
6+
Function views
7+
1. Add an import: from my_app import views
8+
2. Add a URL to urlpatterns: path('', views.home, name='home')
9+
Class-based views
10+
1. Add an import: from other_app.views import Home
11+
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
12+
Including another URLconf
13+
1. Import the include() function: from django.urls import include, path
14+
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
15+
"""
16+
from django.contrib import admin
17+
from django.urls import include, path
18+
from djangotest.testapp import views
19+
20+
urlpatterns = [
21+
path('admin/', admin.site.urls),
22+
path(
23+
'api-auth/',
24+
include('rest_framework.urls', namespace='rest_framework'),
25+
),
26+
path(
27+
'test/<int:pk>',
28+
views.TestView.as_view(),
29+
name='test',
30+
),
31+
]
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
openapi: '3.0.0'
2+
info:
3+
version: '0.0.1'
4+
title: Test Service
5+
paths:
6+
'/test/{pk}':
7+
get:
8+
responses:
9+
'200':
10+
description: Default
11+
content:
12+
application/json:
13+
schema:
14+
type: object
15+
properties:
16+
test:
17+
type: string
18+
required:
19+
- test
20+
parameters:
21+
- required: true
22+
in: path
23+
name: pk
24+
schema:
25+
type: integer
26+
minimum: 1
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import pytest
2+
3+
from six import b
4+
5+
6+
class TestDjangoRESTFrameworkAPIView(object):
7+
8+
@pytest.fixture
9+
def api_request_factory(self):
10+
from rest_framework.test import APIRequestFactory
11+
return APIRequestFactory()
12+
13+
def test_get(self, api_request_factory):
14+
from djangoproject.testapp.views import TestView
15+
view = TestView.as_view()
16+
request = api_request_factory.get('/test/4')
17+
18+
response = view(request, pk='4')
19+
20+
assert response.content == b('{"test": "test_val"}')

0 commit comments

Comments
 (0)