Skip to content

Commit 4afd64d

Browse files
authored
feat: Creates tests for the example app - Redo (#59)
* redoing example tests * checked format * added a line in changelog and added test to makefile * Revert "added a line in changelog and added test to makefile" This reverts commit 2fc7cf0. * added to changelog * added example test to makefile * added a new makefile in example directory * Revert "added a new makefile in example directory" This reverts commit 44d6bf1. * added makefiles * added install requirements to makefile
1 parent 70a263d commit 4afd64d

File tree

10 files changed

+208
-11
lines changed

10 files changed

+208
-11
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ and this project adheres to [Semantic
66
Versioning](https://semver.org/spec/v2.0.0.html).
77

88
# [unreleased] - YYYY-MM-DD
9+
### Added
10+
- [PR 59](https://github.com/salesforce/django-declarative-apis/pull/59) Add test cases for example app's model and responses
11+
912
### Fixed
1013
- [PR 55](https://github.com/salesforce/django-declarative-apis/pull/55) Backwards compatibility fix for field expansion headers, update example app
1114

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ readme:
3434

3535
# Test targets
3636

37-
test-all: coverage vuln-static formatcheck
37+
test-all: coverage vuln-static formatcheck subsystem
3838
.PHONY: test-all
3939

4040
test:
@@ -60,3 +60,6 @@ vuln-static:
6060
formatcheck:
6161
${FORMATCHECK_CMD}
6262
.PHONY: formatcheck
63+
64+
subsystem:
65+
$(MAKE) -C example

example/Makefile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
COVERAGE_CMD = coverage run manage.py test --noinput && coverage xml && coverage report
2+
3+
install:
4+
pip install -r requirements.txt
5+
.PHONY: install
6+
7+
example-tests: coverage
8+
.PHONY: example-tests
9+
10+
coverage:
11+
${COVERAGE_CMD}
12+
.PHONY: coverage
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Generated by Django 2.2.23 on 2021-06-03 13:22
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
initial = True
10+
11+
dependencies = [("django_declarative_apis", "0002_add_consumer_type_field")]
12+
13+
operations = [
14+
migrations.CreateModel(
15+
name="User",
16+
fields=[
17+
(
18+
"id",
19+
models.AutoField(
20+
auto_created=True,
21+
primary_key=True,
22+
serialize=False,
23+
verbose_name="ID",
24+
),
25+
),
26+
("name", models.CharField(max_length=50)),
27+
(
28+
"consumer",
29+
models.ForeignKey(
30+
on_delete=django.db.models.deletion.CASCADE,
31+
to="django_declarative_apis.OauthConsumer",
32+
),
33+
),
34+
],
35+
)
36+
]

example/myapp/tests.py

Lines changed: 0 additions & 10 deletions
This file was deleted.

example/myapp/tests/__init__.py

Whitespace-only changes.

example/myapp/tests/test_models.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from django.test import TestCase
2+
from django_declarative_apis import models
3+
4+
from myapp.models import User
5+
6+
7+
class ModelsTestCase(TestCase):
8+
def test_create_user(self):
9+
consumer = models.OauthConsumer.objects.create(name="smith")
10+
11+
user = User(consumer=consumer, name="smith")
12+
user.save()
13+
14+
self.assertEqual(user.consumer.content_type_id, consumer.content_type_id)
15+
self.assertEqual(user.consumer.id, consumer.id)
16+
self.assertEqual(user.consumer.key, consumer.key)
17+
self.assertEqual(user.consumer.name, consumer.name)
18+
self.assertEqual(user.consumer.object_id, consumer.object_id)
19+
self.assertEqual(user.consumer.secret, consumer.secret)
20+
self.assertEqual(user.consumer.type, consumer.type)
21+
self.assertEqual(user.name, "smith")

example/myapp/tests/test_responses.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import http
2+
3+
from django.test import TestCase
4+
from .testutils import OAuthClient
5+
from django_declarative_apis.models import OauthConsumer
6+
7+
8+
class ResponseTestCase(TestCase):
9+
def test_ping_definition(self):
10+
resp = self.client.get("/ping")
11+
self.assertEqual(resp.json(), {"ping": "pong"})
12+
13+
def test_create_me(self):
14+
self.client = OAuthClient()
15+
consumer = OauthConsumer.objects.create(name="smith")
16+
17+
resp = self.client.post("/me", {}, consumer=consumer, secure=True)
18+
self.assertEqual(resp.status_code, http.HTTPStatus.OK)
19+
self.assertEqual(resp.json()["key"], consumer.key)
20+
self.assertEqual(resp.json()["name"], consumer.name)

example/myapp/tests/testutils.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import oauth2
2+
import oauthlib
3+
import time
4+
5+
from django.test.client import ClientHandler
6+
from django.http import QueryDict
7+
from django import test
8+
from django_declarative_apis.models import OauthConsumer
9+
10+
11+
class OAuthClientHandler(ClientHandler):
12+
def __init__(self, *args, **kwargs):
13+
super(OAuthClientHandler, self).__init__(*args, **kwargs)
14+
15+
def get_response(self, request):
16+
consumer = OauthConsumer.objects.get(name="smith")
17+
18+
# Make request params mutable so we can add authorization parameters.
19+
# We make the params immutable again before processing the request
20+
request.POST = request.POST.copy()
21+
request.GET = request.GET.copy()
22+
23+
if consumer:
24+
if request.method == "POST":
25+
data = request.POST
26+
else:
27+
data = request.GET
28+
29+
# This provides a way for us to override default values for testing.
30+
oauth_version = request.META.get("oauth_version", "1.0")
31+
oauth_nonce = request.META.get("oauth_nonce", oauth2.generate_nonce())
32+
oauth_client_timestamp = request.META.get(
33+
"oauth_timestamp", int(time.time())
34+
)
35+
36+
rsa_key = request.META.get("rsa_key", None)
37+
oauth_signature_method = oauthlib.oauth1.SIGNATURE_HMAC
38+
39+
oauth_signature_data = {
40+
"oauth_version": oauth_version,
41+
"oauth_nonce": oauth_nonce,
42+
"oauth_timestamp": str(oauth_client_timestamp),
43+
"oauth_consumer_key": consumer.key,
44+
"oauth_signature_method": oauth_signature_method,
45+
}
46+
47+
# collect ALL request parameters (original + OAuth) for signing
48+
all_request_parameters = data.copy()
49+
all_request_parameters.update(oauth_signature_data)
50+
51+
# use HMAC-SHA1 signature method
52+
oauth_signature_data.update({"oauth_signature_method": "HMAC-SHA1"})
53+
54+
# Create oauth request object to compute signature
55+
oauth_request = oauth2.Request.from_consumer_and_token(
56+
consumer,
57+
None,
58+
request.method,
59+
request.build_absolute_uri(request.path),
60+
all_request_parameters,
61+
is_form_encoded=True,
62+
)
63+
64+
# Add signature to django request
65+
signature_method = oauth2.SignatureMethod_HMAC_SHA1()
66+
oauth_request.sign_request(signature_method, consumer, None)
67+
oauth_signature_data["oauth_signature"] = oauth_request.get_parameter(
68+
"oauth_signature"
69+
).decode("utf-8")
70+
71+
use_auth_header_signature = request.META.pop(
72+
"use_auth_header_signature", False
73+
)
74+
if use_auth_header_signature:
75+
auth_header_string = "OAuth " + ",".join(
76+
[
77+
'{0}="{1}"'.format(key, value)
78+
for key, value in oauth_signature_data.items()
79+
]
80+
)
81+
request.META["HTTP_AUTHORIZATION"] = auth_header_string
82+
else:
83+
data.update(oauth_signature_data)
84+
85+
# Recreate the GET and POST QueryDicts to make them immutable, as in production
86+
request.POST = QueryDict(request.POST.urlencode().encode("utf-8"))
87+
request.GET = QueryDict(request.GET.urlencode().encode("utf-8"))
88+
89+
return ClientHandler.get_response(self, request)
90+
91+
92+
class OAuthClient(test.Client):
93+
def __init__(self, *args, **kwargs):
94+
test.Client.__init__(self, *args, **kwargs)
95+
self.handler = OAuthClientHandler()
96+
97+
def request(self, **kwargs):
98+
response = super(OAuthClient, self).request(**kwargs)
99+
100+
return response
101+
102+
def post(self, path, data=None, **kwargs):
103+
if "content_type" not in kwargs:
104+
data = data or {}
105+
kwargs["content_type"] = "application/x-www-form-urlencoded"
106+
data_qd = QueryDict(mutable=True)
107+
data_qd.update(data)
108+
data = data_qd.urlencode()
109+
110+
return super(OAuthClient, self).post(path, data, **kwargs)

example/requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
../../django-declarative-apis
22
Django~=2.2.0
33
django-sslserver==0.20
4+
oauth2==1.9.0.post1
5+
oauthlib==2.0.6

0 commit comments

Comments
 (0)