Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit 04a1132

Browse files
authored
Merge pull request #220 from cloudant/65-raise-for-status-exception-handling
Added raise_for_status to improve error handling
2 parents e4d6ae0 + 67b0099 commit 04a1132

File tree

7 files changed

+108
-4
lines changed

7 files changed

+108
-4
lines changed

CHANGES.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
2.2.0 (Unreleased)
22
==================
3+
- [FIXED] HTTPError is raised when 4xx or 5xx codes are encountered
34

45
2.1.0 (2016-08-31)
56
==================

src/cloudant/database.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,10 @@ def exists(self):
101101
102102
:returns: Boolean True if the database exists, False otherwise
103103
"""
104-
resp = self.r_session.get(self.database_url)
104+
resp = self.r_session.head(self.database_url)
105+
if resp.status_code not in [200, 404]:
106+
resp.raise_for_status()
107+
105108
return resp.status_code == 200
106109

107110
def metadata(self):
@@ -891,6 +894,7 @@ def update_handler_result(self, ddoc_id, handler_name, doc_id=None, data=None, *
891894
resp = self.r_session.post(
892895
'/'.join([ddoc.document_url, '_update', handler_name]),
893896
params=params, data=data)
897+
resp.raise_for_status()
894898
return resp.text
895899

896900
class CloudantDatabase(CouchDatabase):

src/cloudant/design_document.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,7 @@ def info(self):
715715
"""
716716
ddoc_info = self.r_session.get(
717717
'/'.join([self.document_url, '_info']))
718+
ddoc_info.raise_for_status()
718719
return ddoc_info.json()
719720

720721
def search_info(self, search_index):
@@ -726,4 +727,5 @@ def search_info(self, search_index):
726727
"""
727728
ddoc_search_info = self.r_session.get(
728729
'/'.join([self.document_url, '_search_info', search_index]))
730+
ddoc_search_info.raise_for_status()
729731
return ddoc_search_info.json()

src/cloudant/document.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,11 @@ def exists(self):
101101
"""
102102
if self._document_id is None:
103103
return False
104-
resp = self.r_session.get(self.document_url)
104+
else:
105+
resp = self.r_session.head(self.document_url)
106+
if resp.status_code not in [200, 404]:
107+
resp.raise_for_status()
108+
105109
return resp.status_code == 200
106110

107111
def json(self):

tests/unit/database_tests.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"""
2424

2525
import unittest
26+
import mock
2627
import requests
2728
import posixpath
2829
import os
@@ -92,13 +93,28 @@ def test_retrieve_creds(self):
9293

9394
def test_exists(self):
9495
"""
95-
Test database exists fucntionality
96+
Tests that the result of True is expected when the database exists,
97+
and False is expected when the database is nonexistent remotely.
9698
"""
9799
self.assertTrue(self.db.exists())
98100
# Construct a database object that does not exist remotely
99101
fake_db = self.client._DATABASE_CLASS(self.client, 'no-such-db')
100102
self.assertFalse(fake_db.exists())
101103

104+
def test_exists_raises_httperror(self):
105+
"""
106+
Test database exists raises an HTTPError.
107+
"""
108+
# Mock HTTPError when running against CouchDB and Cloudant
109+
resp = requests.Response()
110+
resp.status_code = 400
111+
self.client.r_session.head = mock.Mock(return_value=resp)
112+
with self.assertRaises(requests.HTTPError) as cm:
113+
self.db.exists()
114+
err = cm.exception
115+
self.assertEqual(err.response.status_code, 400)
116+
self.client.r_session.head.assert_called_with(self.db.database_url)
117+
102118
def test_create_db_delete_db(self):
103119
"""
104120
Test creating and deleting a database
@@ -749,6 +765,26 @@ def test_update_doc_with_update_handler(self):
749765
'message': 'hello'}
750766
)
751767

768+
def test_update_handler_raises_httperror(self):
769+
"""
770+
Test update_handler_result raises an HTTPError.
771+
"""
772+
# Mock HTTPError when running against CouchDB or Cloudant
773+
resp = requests.Response()
774+
resp.status_code = 400
775+
self.client.r_session.put = mock.Mock(return_value=resp)
776+
with self.assertRaises(requests.HTTPError) as cm:
777+
self.db.update_handler_result('ddoc001', 'update001', 'julia001',
778+
field='new_field', value='new_value',
779+
data={'message': 'hello'})
780+
err = cm.exception
781+
self.assertEqual(err.response.status_code, 400)
782+
ddoc = DesignDocument(self.db, 'ddoc001')
783+
self.client.r_session.put.assert_called_with(
784+
'/'.join([ddoc.document_url, '_update', 'update001', 'julia001']),
785+
data={'message': 'hello'},
786+
params={'field': 'new_field', 'value': 'new_value'})
787+
752788
@unittest.skipUnless(
753789
os.environ.get('RUN_CLOUDANT_TESTS') is not None,
754790
'Skipping Cloudant specific Database tests'

tests/unit/design_document_tests.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import json
2525
import os
2626
import unittest
27+
import mock
28+
import requests
2729

2830
from cloudant.document import Document
2931
from cloudant.design_document import DesignDocument
@@ -754,6 +756,22 @@ def test_get_info(self):
754756
'name': name
755757
})
756758

759+
def test_get_info_raises_httperror(self):
760+
"""
761+
Test get_info raises an HTTPError.
762+
"""
763+
# Mock HTTPError when running against CouchDB and Cloudant
764+
resp = requests.Response()
765+
resp.status_code = 400
766+
self.client.r_session.get = mock.Mock(return_value=resp)
767+
ddoc = DesignDocument(self.db, '_design/ddoc001')
768+
with self.assertRaises(requests.HTTPError) as cm:
769+
ddoc.info()
770+
err = cm.exception
771+
self.assertEqual(err.response.status_code, 400)
772+
self.client.r_session.get.assert_called_with(
773+
'/'.join([ddoc.document_url, '_info']))
774+
757775
@unittest.skipUnless(
758776
os.environ.get('RUN_CLOUDANT_TESTS') is not None,
759777
'Skipping Cloudant _search_info endpoint test'
@@ -781,6 +799,27 @@ def test_get_search_info(self):
781799
'pending_seq': 101, 'committed_seq': 0},
782800
})
783801

802+
@unittest.skipUnless(
803+
os.environ.get('RUN_CLOUDANT_TESTS') is not None,
804+
'Skipping Cloudant _search_info raises HTTPError test'
805+
)
806+
def test_get_search_info_raises_httperror(self):
807+
"""
808+
Test get_search_info raises an HTTPError.
809+
"""
810+
# Mock HTTPError when running against Cloudant
811+
search_index = 'search001'
812+
resp = requests.Response()
813+
resp.status_code = 400
814+
self.client.r_session.get = mock.Mock(return_value=resp)
815+
ddoc = DesignDocument(self.db, '_design/ddoc001')
816+
with self.assertRaises(requests.HTTPError) as cm:
817+
ddoc.search_info(search_index)
818+
err = cm.exception
819+
self.assertEqual(err.response.status_code, 400)
820+
self.client.r_session.get.assert_called_with(
821+
'/'.join([ddoc.document_url, '_search_info', search_index]))
822+
784823
def test_add_a_search_index(self):
785824
"""
786825
Test that adding a search index adds a search index object to

tests/unit/document_tests.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"""
2424

2525
import unittest
26+
import mock
2627
import posixpath
2728
import json
2829
import requests
@@ -128,7 +129,8 @@ def test_constructor_without_docid(self):
128129

129130
def test_document_exists(self):
130131
"""
131-
Test whether a document exists remotely
132+
Tests that the result of True is expected when the document exists,
133+
and False is expected when the document is nonexistent remotely.
132134
"""
133135
doc = Document(self.db)
134136
self.assertFalse(doc.exists())
@@ -137,6 +139,22 @@ def test_document_exists(self):
137139
doc.create()
138140
self.assertTrue(doc.exists())
139141

142+
def test_document_exists_raises_httperror(self):
143+
"""
144+
Test document exists raises an HTTPError.
145+
"""
146+
# Mock HTTPError when running against CouchDB and Cloudant
147+
resp = requests.Response()
148+
resp.status_code = 400
149+
self.client.r_session.head = mock.Mock(return_value=resp)
150+
doc = Document(self.db)
151+
doc['_id'] = 'julia006'
152+
with self.assertRaises(requests.HTTPError) as cm:
153+
doc.exists()
154+
err = cm.exception
155+
self.assertEqual(err.response.status_code, 400)
156+
self.client.r_session.head.assert_called_with(doc.document_url)
157+
140158
def test_retrieve_document_json(self):
141159
"""
142160
Test the document dictionary renders as json appropriately

0 commit comments

Comments
 (0)