From 1c4ee989f724495f6d917ed62a878b09c286505c Mon Sep 17 00:00:00 2001 From: medmunds Date: Fri, 16 Jan 2015 13:36:00 -0800 Subject: [PATCH] Fix "the JSON object must be str, not 'bytes'" on python3. Closes #82. * Test mock API responses (with actual content) in admin tests. (This exposes failure case, at least under Python 3.4/Django 1.6.) * Parse json from Response.text, rather than raw Response.content bytes, in admin views. --- djrill/tests/test_admin.py | 74 +++++++++++++++++++++++++++++++++++--- djrill/views.py | 6 ++-- 2 files changed, 73 insertions(+), 7 deletions(-) diff --git a/djrill/tests/test_admin.py b/djrill/tests/test_admin.py index d27f871..95a7978 100644 --- a/djrill/tests/test_admin.py +++ b/djrill/tests/test_admin.py @@ -3,6 +3,7 @@ import sys from django.test import TestCase from django.contrib.auth.models import User from django.contrib import admin +import six from djrill.tests.mock_backend import DjrillBackendMockAPITestCase @@ -17,10 +18,6 @@ def reset_admin_site(): class DjrillAdminTests(DjrillBackendMockAPITestCase): """Test the Djrill admin site""" - # These tests currently just verify that the admin site pages load - # without error -- they don't test any Mandrill-supplied content. - # (Future improvements could mock the Mandrill responses.) - # These urls set up the DjrillAdminSite as suggested in the readme urls = 'djrill.tests.admin_urls' @@ -39,24 +36,32 @@ class DjrillAdminTests(DjrillBackendMockAPITestCase): self.client.login(username='admin', password='secret') def test_admin_senders(self): + self.mock_post.return_value = self.MockResponse(raw=self.mock_api_content['users/senders.json']) response = self.client.get('/admin/djrill/senders/') self.assertEqual(response.status_code, 200) self.assertContains(response, "Senders") + self.assertContains(response, "sender.example@mandrillapp.com") def test_admin_status(self): + self.mock_post.return_value = self.MockResponse(raw=self.mock_api_content['users/info.json']) response = self.client.get('/admin/djrill/status/') self.assertEqual(response.status_code, 200) self.assertContains(response, "Status") + self.assertContains(response, "myusername") def test_admin_tags(self): + self.mock_post.return_value = self.MockResponse(raw=self.mock_api_content['tags/list.json']) response = self.client.get('/admin/djrill/tags/') self.assertEqual(response.status_code, 200) self.assertContains(response, "Tags") + self.assertContains(response, "example-tag") def test_admin_urls(self): + self.mock_post.return_value = self.MockResponse(raw=self.mock_api_content['urls/list.json']) response = self.client.get('/admin/djrill/urls/') self.assertEqual(response.status_code, 200) self.assertContains(response, "URLs") + self.assertContains(response, "example.com/example-page") def test_admin_index(self): """Make sure Djrill section is included in the admin index page""" @@ -65,6 +70,67 @@ class DjrillAdminTests(DjrillBackendMockAPITestCase): self.assertContains(response, "Djrill") + mock_api_content = { + 'users/senders.json': six.b(''' + [ + { + "address": "sender.example@mandrillapp.com", + "created_at": "2013-01-01 15:30:27", + "sent": 42, "hard_bounces": 42, "soft_bounces": 42, "rejects": 42, "complaints": 42, + "unsubs": 42, "opens": 42, "clicks": 42, "unique_opens": 42, "unique_clicks": 42 + } + ] + '''), + + 'users/info.json': six.b(''' + { + "username": "myusername", + "created_at": "2013-01-01 15:30:27", + "public_id": "aaabbbccc112233", + "reputation": 42, + "hourly_quota": 42, + "backlog": 42, + "stats": { + "today": { "sent": 42, "hard_bounces": 42, "soft_bounces": 42, "rejects": 42, "complaints": 42, + "unsubs": 42, "opens": 42, "unique_opens": 42, "clicks": 42, "unique_clicks": 42 }, + "last_7_days": { "sent": 42, "hard_bounces": 42, "soft_bounces": 42, "rejects": 42, "complaints": 42, + "unsubs": 42, "opens": 42, "unique_opens": 42, "clicks": 42, "unique_clicks": 42 }, + "last_30_days": { "sent": 42, "hard_bounces": 42, "soft_bounces": 42, "rejects": 42, "complaints": 42, + "unsubs": 42, "opens": 42, "unique_opens": 42, "clicks": 42, "unique_clicks": 42 }, + "last_60_days": { "sent": 42, "hard_bounces": 42, "soft_bounces": 42, "rejects": 42, "complaints": 42, + "unsubs": 42, "opens": 42, "unique_opens": 42, "clicks": 42, "unique_clicks": 42 }, + "last_90_days": { "sent": 42, "hard_bounces": 42, "soft_bounces": 42, "rejects": 42, "complaints": 42, + "unsubs": 42, "opens": 42, "unique_opens": 42, "clicks": 42, "unique_clicks": 42 }, + "all_time": { "sent": 42, "hard_bounces": 42, "soft_bounces": 42, "rejects": 42, "complaints": 42, + "unsubs": 42, "opens": 42, "unique_opens": 42, "clicks": 42, "unique_clicks": 42 } + } + } + '''), + + 'tags/list.json': six.b(''' + [ + { + "tag": "example-tag", + "reputation": 42, + "sent": 42, "hard_bounces": 42, "soft_bounces": 42, "rejects": 42, "complaints": 42, + "unsubs": 42, "opens": 42, "clicks": 42, "unique_opens": 42, "unique_clicks": 42 + } + ] + '''), + + 'urls/list.json': six.b(''' + [ + { + "url": "http://example.com/example-page", + "sent": 42, + "clicks": 42, + "unique_clicks": 42 + } + ] + '''), + } + + class DjrillNoAdminTests(TestCase): def test_admin_autodiscover_without_djrill(self): """Make sure autodiscover doesn't die without DjrillAdminSite""" diff --git a/djrill/views.py b/djrill/views.py index 3e76b85..3caaec9 100644 --- a/djrill/views.py +++ b/djrill/views.py @@ -73,7 +73,7 @@ class DjrillApiJsonObjectsMixin(object): req = requests.post("%s/%s" % (self.api_url, api_uri), data=payload) if req.status_code == 200: - return req.content + return req.text messages.error(self.request, self._api_error_handler(req)) return json.dumps("error") @@ -81,7 +81,7 @@ class DjrillApiJsonObjectsMixin(object): """ If the API returns an error, display it to the user. """ - content = json.loads(req.content) + content = json.loads(req.text) return "Mandrill returned a %d response: %s" % (req.status_code, content["message"]) @@ -147,7 +147,7 @@ class DjrillIndexView(DjrillApiMixin, TemplateView): payload = json.dumps({"key": self.api_key}) req = requests.post("%s/users/info.json" % self.api_url, data=payload) - return self.render_to_response({"status": json.loads(req.content)}) + return self.render_to_response({"status": json.loads(req.text)}) class DjrillSendersListView(DjrillAdminMedia, DjrillApiMixin,