Merge lp:~mandel/desktopcouch/mock_db_for_records into lp:desktopcouch

Proposed by Manuel de la Peña
Status: Merged
Approved by: Manuel de la Peña
Approved revision: 251
Merged at revision: 248
Proposed branch: lp:~mandel/desktopcouch/mock_db_for_records
Merge into: lp:desktopcouch
Diff against target: 726 lines (+561/-28)
4 files modified
desktopcouch/application/server.py (+2/-2)
desktopcouch/records/database.py (+39/-25)
desktopcouch/records/tests/test_mocked_server.py (+519/-0)
desktopcouch/records/tests/test_server.py (+1/-1)
To merge this branch: bzr merge lp:~mandel/desktopcouch/mock_db_for_records
Reviewer Review Type Date Requested Status
Eric Casteleijn (community) Approve
Vincenzo Di Somma (community) Approve
Review via email: mp+45262@code.launchpad.net

Commit message

Moved trash db to be an instance variable for bug #697661
Fixed how deleted records ids are generated for bug #697636
Added unit tests in which the db access is mocked for bug #674587

Description of the change

Moved trash db to be an instance variable for bug #697661
Fixed how deleted records ids are generated for bug #697636
Added unit tests in which the db access is mocked for bug #674587

To post a comment you must log in.
Revision history for this message
Vincenzo Di Somma (vds) :
review: Approve
Revision history for this message
Eric Casteleijn (thisfred) wrote :

I like it!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'desktopcouch/application/server.py'
--- desktopcouch/application/server.py 2010-11-26 20:30:30 +0000
+++ desktopcouch/application/server.py 2011-01-05 17:11:33 +0000
@@ -64,7 +64,7 @@
6464
65 def __init__(self, database, uri=None, record_factory=None,65 def __init__(self, database, uri=None, record_factory=None,
66 create=False, server_class=DesktopServer, oauth_tokens=None,66 create=False, server_class=DesktopServer, oauth_tokens=None,
67 ctx=None):67 ctx=None, views_factory=None):
68 if ctx is None:68 if ctx is None:
69 ctx = DEFAULT_CONTEXT69 ctx = DEFAULT_CONTEXT
70 self.ctx = ctx70 self.ctx = ctx
@@ -72,7 +72,7 @@
72 super(DesktopDatabase, self).__init__(72 super(DesktopDatabase, self).__init__(
73 database, uri, record_factory=record_factory,73 database, uri, record_factory=record_factory,
74 create=create, server_class=server_class,74 create=create, server_class=server_class,
75 oauth_tokens=oauth_tokens, ctx=ctx)75 oauth_tokens=oauth_tokens, ctx=ctx, views_factory=views_factory)
7676
77 # pylint: disable=W022177 # pylint: disable=W0221
78 # Arguments number differs from overridden method78 # Arguments number differs from overridden method
7979
=== modified file 'desktopcouch/records/database.py'
--- desktopcouch/records/database.py 2011-01-05 13:48:31 +0000
+++ desktopcouch/records/database.py 2011-01-05 17:11:33 +0000
@@ -26,7 +26,6 @@
26# pylint: disable=W040226# pylint: disable=W0402
27import string27import string
28# pylint: enable=W040228# pylint: enable=W0402
29import uuid
30import warnings29import warnings
3130
32from time import time31from time import time
@@ -106,7 +105,12 @@
106 """A desktopcouch.records specific CouchDb database."""105 """A desktopcouch.records specific CouchDb database."""
107106
108 def __init__(self, database, uri, record_factory=None, create=False,107 def __init__(self, database, uri, record_factory=None, create=False,
109 server_class=Server, **server_class_extras):108 views_factory=None, server_class=Server,
109 **server_class_extras):
110 if views_factory is None:
111 self._views_factory = ViewDefinition
112 else:
113 self._views_factory = views_factory
110 self.server_uri = uri114 self.server_uri = uri
111 self._database_name = database115 self._database_name = database
112 self.record_factory = record_factory or Record116 self.record_factory = record_factory or Record
@@ -115,6 +119,7 @@
115 self._server_class_extras = server_class_extras119 self._server_class_extras = server_class_extras
116 self._server = None120 self._server = None
117 self.db = None121 self.db = None
122 self._dctrash = None
118 self._reconnect()123 self._reconnect()
119 self._changes_since = self.db.info()["update_seq"]124 self._changes_since = self.db.info()["update_seq"]
120 self._changes_last_used = 0 # Immediate run works.125 self._changes_last_used = 0 # Immediate run works.
@@ -182,40 +187,45 @@
182187
183 def put_record(self, record):188 def put_record(self, record):
184 """Put a record in back end storage."""189 """Put a record in back end storage."""
185 if not record.record_id:190 record_id = record.record_id
191 record_data = record._data # pylint: disable=W0212
192 if not record_id:
186 # Do not rely on couchdb to create an ID for us.193 # Do not rely on couchdb to create an ID for us.
187 # pylint: disable=E1101194 # pylint: disable=E1101
188 record.record_id = base_n(uuid4().int, 62)195 record_id = base_n(uuid4().int, 62)
196 record.record_id = record_id
189 # pylint: enable=E1101197 # pylint: enable=E1101
190 self.db[record.record_id] = record._data # pylint: disable=W0212198 self.db[record_id] = record_data
191199
192 # pylint: disable=W0212200 # pylint: disable=W0212
193 for attachment_name in getattr(record, "_detached", []):201 for attachment_name in getattr(record, "_detached", []):
194 self.db.delete_attachment(record._data, attachment_name)202 self.db.delete_attachment(record_data, attachment_name)
195203
196 for attachment_name in record.list_attachments():204 for attachment_name in record.list_attachments():
197 data, content_type = record.attachment_data(attachment_name)205 data, content_type = record.attachment_data(attachment_name)
198 self.db.put_attachment(record._data,206 self.db.put_attachment(record_data,
199 data,207 data,
200 attachment_name,208 attachment_name,
201 content_type)209 content_type)
202 # pylint: enable=W0212210 # pylint: enable=W0212
203211
204 return record.record_id212 return record_id
205213
206 def put_records_batch(self, batch):214 def put_records_batch(self, batch):
207 """Put a batch of records in back end storage."""215 """Put a batch of records in back end storage."""
208 # used to access fast the record216 # used to access fast the record
209 records_hash = {}217 records_hash = {}
210 for record in batch:218 for record in batch:
211 if not record.record_id:219 record_id = record.record_id
220 if not record_id:
212 # Do not rely on couchdb to create an ID for us.221 # Do not rely on couchdb to create an ID for us.
213 # pylint: disable=E1101222 # pylint: disable=E1101
214 record.record_id = base_n(uuid4().int, 62)223 record_id = base_n(uuid4().int, 62)
224 record.record_id = record_id
215 # pylint: enable=E1101225 # pylint: enable=E1101
216 records_hash[record.record_id] = record226 records_hash[record_id] = record
217 # although with a single record we need to test for the227 # although with a single record we need to test for the
218 # revisison, with a batch we do not, but we have to make sure228 # revision, with a batch we do not, but we have to make sure
219 # that we did not get an error229 # that we did not get an error
220 # pylint: disable=W0212230 # pylint: disable=W0212
221 batch_put_result = self.db.update([record._data for record in batch])231 batch_put_result = self.db.update([record._data for record in batch])
@@ -223,15 +233,16 @@
223 success, docid, rev_or_exc = current_tuple233 success, docid, rev_or_exc = current_tuple
224 if success:234 if success:
225 record = records_hash[docid]235 record = records_hash[docid]
236 record_data = record._data
226 # set the new rev237 # set the new rev
227 record._data["_rev"] = rev_or_exc238 record_data["_rev"] = rev_or_exc
228 for attachment_name in getattr(record, "_detached", []):239 for attachment_name in getattr(record, "_detached", []):
229 self.db.delete_attachment(record._data, attachment_name)240 self.db.delete_attachment(record_data, attachment_name)
230 for attachment_name in record.list_attachments():241 for attachment_name in record.list_attachments():
231 data, content_type = record.attachment_data(242 data, content_type = record.attachment_data(
232 attachment_name)243 attachment_name)
233 self.db.put_attachment(244 self.db.put_attachment(
234 {"_id": record.record_id, "_rev": record["_rev"]},245 {"_id": docid, "_rev": rev_or_exc},
235 data, attachment_name, content_type)246 data, attachment_name, content_type)
236 # pylint: enable=W0212247 # pylint: enable=W0212
237 # all success record have the blobs added we return result of248 # all success record have the blobs added we return result of
@@ -323,12 +334,15 @@
323 """Delete record with given id"""334 """Delete record with given id"""
324 record = self.get_record(record_id)335 record = self.get_record(record_id)
325 new_record = copy.deepcopy(record)336 new_record = copy.deepcopy(record)
326 dctrash = self.__class__(337 if not self._dctrash:
327 database=DCTRASH,338 self._dctrash = self.__class__(
328 uri=self.server_uri,339 database=DCTRASH,
329 create=True,340 uri=self.server_uri,
330 **self._server_class_extras)341 create=True,
331 new_record.record_id = str(uuid.uuid4())342 **self._server_class_extras)
343 # pylint: disable=E1101
344 new_record.record_id = base_n(uuid4().int, 62)
345 # pylint: enable=E1101
332 del new_record._data['_rev']346 del new_record._data['_rev']
333 try:347 try:
334 del new_record._data['_attachments']348 del new_record._data['_attachments']
@@ -339,7 +353,7 @@
339 {'original_database_name': self._database_name,353 {'original_database_name': self._database_name,
340 'original_id': record_id}}354 'original_id': record_id}}
341 del self.db[record_id]355 del self.db[record_id]
342 return dctrash.put_record(new_record)356 return self._dctrash.put_record(new_record)
343 # pylint: enable=W0212357 # pylint: enable=W0212
344358
345 def delete_view(self, view_name, design_doc=DEFAULT_DESIGN_DOCUMENT):359 def delete_view(self, view_name, design_doc=DEFAULT_DESIGN_DOCUMENT):
@@ -362,7 +376,7 @@
362 if len(view_container) > 0:376 if len(view_container) > 0:
363 # Construct a new list of objects representing all views to have.377 # Construct a new list of objects representing all views to have.
364 views = [378 views = [
365 ViewDefinition(design_doc, k, v.get("map"),379 self._views_factory(design_doc, k, v.get("map"),
366 v.get("reduce"))380 v.get("reduce"))
367 for k, v in view_container.iteritems()]381 for k, v in view_container.iteritems()]
368 # Push back a new batch of view. Pray to Eris that this doesn't382 # Push back a new batch of view. Pray to Eris that this doesn't
@@ -372,7 +386,7 @@
372 # its design-document from the ViewDefinition items, and if there386 # its design-document from the ViewDefinition items, and if there
373 # are no items, then it has no idea of a design document to387 # are no items, then it has no idea of a design document to
374 # update. This is a serious flaw. Thus, the "else" to follow.388 # update. This is a serious flaw. Thus, the "else" to follow.
375 ViewDefinition.sync_many(self.db, views, remove_missing=True)389 self._views_factory.sync_many(self.db, views, remove_missing=True)
376 else:390 else:
377 # There are no views left in this design document.391 # There are no views left in this design document.
378392
@@ -401,7 +415,7 @@
401 if design_doc is None:415 if design_doc is None:
402 design_doc = view_name416 design_doc = view_name
403417
404 view = ViewDefinition(design_doc, view_name, map_js, reduce_js)418 view = self._views_factory(design_doc, view_name, map_js, reduce_js)
405 view.sync(self.db)419 view.sync(self.db)
406 assert self.view_exists(view_name, design_doc)420 assert self.view_exists(view_name, design_doc)
407421
408422
=== added file 'desktopcouch/records/tests/test_mocked_server.py'
--- desktopcouch/records/tests/test_mocked_server.py 1970-01-01 00:00:00 +0000
+++ desktopcouch/records/tests/test_mocked_server.py 2011-01-05 17:11:33 +0000
@@ -0,0 +1,519 @@
1# Copyright 2009-2010 Canonical Ltd.
2#
3# This file is part of desktopcouch.
4#
5# desktopcouch is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Lesser General Public License version 3
7# as published by the Free Software Foundation.
8#
9# desktopcouch is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Lesser General Public License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with desktopcouch. If not, see <http://www.gnu.org/licenses/>.
16#
17# Authors: Manuel de la Pena<manuel@canonical.com>
18"""Test the database code mocking the db access."""
19
20from couchdb.http import ResourceNotFound
21from mocker import MockerTestCase, ANY, KWARGS
22
23from desktopcouch.application.server import DesktopDatabase
24from desktopcouch.records.database import NoSuchDatabase
25
26
27# disable pylint warnings that are common due to the nature of the tests
28# pylint: disable=W0104, W0201, W0212
29def set_database_expectations(testcase):
30 """Set the expectations for the db creation."""
31 testcase.server_class(testcase.uri, ctx=testcase.ctx,
32 oauth_tokens=testcase.oauth_tokens)
33 testcase.mocker.result(testcase.server_class)
34 testcase.dbname in testcase.server_class
35 testcase.mocker.result(True)
36 testcase.server_class[testcase.dbname]
37 testcase.mocker.result(testcase.server_class)
38 testcase.server_class.info()
39 testcase.mocker.result({'update_seq': []})
40
41
42def set_create_view_expectations(testcase):
43 """Set the expectations for when a view is created."""
44 # set expectations for the creation of the view
45 testcase.views_factory(ANY, ANY, ANY, ANY)
46 testcase.mocker.result(testcase.views_factory)
47 testcase.views_factory.sync(testcase.server_class)
48 # assert that is was added, shall we be mocking this?
49 testcase.server_class[ANY]['views']
50 testcase.mocker.result(testcase.server_class)
51 ANY in testcase.server_class
52 testcase.mocker.result(True)
53
54
55def create_database(testcase):
56 """Create a database to be used in the testcase."""
57 testcase.database = DesktopDatabase(testcase.dbname, uri=testcase.uri,
58 record_factory=testcase.record_factory, create=True,
59 server_class=testcase.server_class,
60 oauth_tokens=testcase.oauth_tokens, ctx=testcase.ctx,
61 views_factory=testcase.views_factory)
62
63
64class TestMockedCouchDatabaseDeprecated(MockerTestCase):
65 """Test the deprecated API."""
66
67 def setUp(self):
68 """Setup each test."""
69 super(TestMockedCouchDatabaseDeprecated, self).setUp()
70 # set mocked objects
71 self.record_factory = self.mocker.mock()
72 self.server_class = self.mocker.mock()
73 self.oauth_tokens = self.mocker.mock()
74 self.ctx = self.mocker.mock()
75 self.views_factory = self.mocker.mock()
76 # set not mocked required data
77 self.uri = 'uri'
78 self.dbname = self._testMethodName
79 self.database = None
80
81 def set_database_expectations(self):
82 """Set the expectations for the creation of the db."""
83 self.server_class(self.uri, ctx=self.ctx,
84 oauth_tokens=self.oauth_tokens)
85 self.mocker.result(self.server_class)
86 self.dbname in self.server_class
87 self.mocker.result(True)
88 self.server_class[self.dbname]
89 self.mocker.result(self.server_class)
90 self.server_class.info()
91 self.mocker.result({'update_seq': []})
92
93 def test_get_records_by_record_type_saved_view(self):
94 """Test getting multiple records by type."""
95 record_type = 'test.com'
96 set_database_expectations(self)
97 # set expectations for the view_exists operation
98 self.server_class[ANY]['views']
99 self.mocker.result(self.server_class)
100 ANY in self.server_class
101 self.mocker.result(True)
102 # set expectations for the execution
103 self.server_class.view(ANY, KWARGS)
104 self.mocker.result({record_type: record_type})
105 self.mocker.replay()
106 create_database(self)
107 records = self.database.get_records(record_type=record_type,
108 create_view=True)
109 self.assertEqual(record_type, records)
110
111 def test_get_records_by_record_type_new_view(self):
112 """Test getting multiple record and create the view."""
113 record_type = 'test.com'
114 set_database_expectations(self)
115 # set expectations for the view_exists operation
116 self.server_class[ANY]['views']
117 self.mocker.result(self.server_class)
118 ANY in self.server_class
119 self.mocker.result(False)
120 set_create_view_expectations(self)
121 # set expectations for the execution
122 self.server_class.view(ANY, KWARGS)
123 self.mocker.result({record_type: record_type})
124 self.mocker.replay()
125 create_database(self)
126 records = self.database.get_records(record_type=record_type,
127 create_view=True)
128 self.assertEqual(record_type, records)
129
130 def test_get_records_by_record_type_new_view_not_create(self):
131 """Test getting the records when the view does not exist."""
132 record_type = 'test.com'
133 set_database_expectations(self)
134 # assert that is was added, shall we be mocking this?
135 self.server_class[ANY]['views']
136 self.mocker.result(self.server_class)
137 ANY in self.server_class
138 self.mocker.result(True)
139 # set expectations for the execution
140 self.server_class.view(ANY, KWARGS)
141 self.mocker.result({record_type: record_type})
142 self.mocker.replay()
143 create_database(self)
144 self.assertRaises(KeyError, self.database.get_records,
145 (record_type, False))
146
147 def test_get_records_by_record_type_saved_view_false_create(self):
148 """Test calling the view and do not create it."""
149 record_type = 'test.com'
150 set_database_expectations(self)
151 # set expectations for the view_exists operation
152 self.server_class[ANY]['views']
153 self.mocker.result(self.server_class)
154 ANY in self.server_class
155 self.mocker.result(True)
156 # set expectations for the execution
157 self.server_class.view(ANY, KWARGS)
158 self.mocker.result({record_type: record_type})
159 self.mocker.replay()
160 create_database(self)
161 records = self.database.get_records(record_type=record_type,
162 create_view=False)
163 self.assertEqual(record_type, records)
164
165
166class TestMockedDesktopDatabase(MockerTestCase):
167 """Test case for DesktopDatabase."""
168
169 def setUp(self):
170 """setup each test"""
171 super(TestMockedDesktopDatabase, self).setUp()
172 # set mocked objects
173 self.record_factory = self.mocker.mock()
174 self.server_class = self.mocker.mock()
175 self.oauth_tokens = self.mocker.mock()
176 self.ctx = self.mocker.mock()
177 self.views_factory = self.mocker.mock()
178 # set not mocked required data
179 self.uri = 'uri'
180 self.dbname = self._testMethodName
181 self.database = None
182
183 def _set_get_all_records_expectations(self, record_type, view_result,
184 records):
185 """Set the common expectations for the get_all_records call."""
186 # replace the transform to records funtion which is used to
187 # return records instead of view results
188 transform_to_records = self.mocker.replace(
189 'desktopcouch.records.database.transform_to_records')
190 self.server_class.view(ANY, KWARGS)
191 self.mocker.result(view_result)
192 transform_to_records(record_type)
193 self.mocker.result(records)
194
195 def test_database_not_exists(self):
196 """Test that the database does not exist."""
197 # set expectations so that the server class returns that
198 # the db does ot exist.
199 self.server_class(self.uri, ctx=self.ctx,
200 oauth_tokens=self.oauth_tokens)
201 self.mocker.result(self.server_class)
202 self.dbname in self.server_class
203 self.mocker.result(False)
204 self.mocker.replay()
205 self.assertRaises(NoSuchDatabase, DesktopDatabase, self.dbname,
206 uri=self.uri, record_factory=self.record_factory, create=False,
207 server_class=self.server_class, oauth_tokens=self.oauth_tokens,
208 ctx=self.ctx, views_factory=self.views_factory)
209
210 def test_get_records_by_record_type_present_view(self):
211 """Test getting mutliple records by type with a present view."""
212 record_type = 'test.com'
213 view_result = {record_type: record_type}
214 records = ['first', 'second']
215 set_database_expectations(self)
216 self._set_get_all_records_expectations(record_type, view_result,
217 records)
218 self.mocker.replay()
219 create_database(self)
220 self.assertEqual(records,
221 self.database.get_all_records(record_type=record_type))
222
223 def test_get_records_by_record_type_missing_view(self):
224 """Test getting mutliple records by type with a present view."""
225 record_type = 'test.com'
226 view_result = {record_type: record_type}
227 records = ['first', 'second']
228 set_database_expectations(self)
229 self.server_class.view(ANY, KWARGS)
230 # raise exception so that the view has to be created.
231 self.mocker.throw(ResourceNotFound)
232 set_create_view_expectations(self)
233 # set the expectations to be used after the creation of the view
234 self._set_get_all_records_expectations(record_type, view_result,
235 records)
236 self.mocker.replay()
237 create_database(self)
238 self.assertEqual(records,
239 self.database.get_all_records(record_type=record_type))
240
241 def test_get_present_record_no_attachments(self):
242 """Test getting a record."""
243 record_id = 'test_record'
244 data = {record_id: record_id}
245 record_mock = self.mocker.mock()
246 set_database_expectations(self)
247 self.server_class[record_id]
248 self.mocker.result(data)
249 self.record_factory(data=data)
250 self.mocker.result(record_mock)
251 self.mocker.replay()
252 create_database(self)
253 self.assertEqual(record_mock, self.database.get_record(record_id))
254
255 def test_get_deleted_record(self):
256 """Test getting a deleted record."""
257 record_id = 'random_id'
258 set_database_expectations(self)
259 self.server_class[record_id]
260 self.mocker.throw(ResourceNotFound)
261 self.mocker.replay()
262 create_database(self)
263 self.assertEqual(None, self.database.get_record(record_id))
264
265 def test_put_record_no_attachments_or_id(self):
266 """Test putting a record with no attachments."""
267 record_id = 'random_id'
268 record_mock = self.mocker.mock()
269 # replace the base_n funtion which is used to
270 # return the string representation of the uuid
271 base_n = self.mocker.replace(
272 'desktopcouch.records.database.base_n')
273 set_database_expectations(self)
274 record_mock.record_id
275 self.mocker.result(None)
276 base_n(ANY, ANY)
277 self.mocker.result(record_id)
278 record_mock.record_id = record_id
279 record_mock._detached
280 self.mocker.result([])
281 record_mock.list_attachments()
282 self.mocker.result([])
283 record_mock._data
284 self.mocker.result({})
285 self.server_class[record_id] = {}
286 self.mocker.replay()
287 create_database(self)
288 self.assertEqual(record_id, self.database.put_record(record_mock))
289
290 def test_put_record_no_attachments(self):
291 """Test putting a record with no attachments."""
292 record_id = 'random_id'
293 record_mock = self.mocker.mock()
294 set_database_expectations(self)
295 record_mock.record_id
296 self.mocker.result(record_id)
297 record_mock._detached
298 self.mocker.result([])
299 record_mock.list_attachments()
300 self.mocker.result([])
301 record_mock._data
302 self.mocker.result({})
303 self.server_class[record_id] = {}
304 self.mocker.replay()
305 create_database(self)
306 self.assertEqual(record_id, self.database.put_record(record_mock))
307
308 def test_put_record_with_attachments(self):
309 """Test putting a record with attachments."""
310 record_id = 'random_id'
311 record_mock = self.mocker.mock()
312 set_database_expectations(self)
313 record_mock.record_id
314 self.mocker.result(record_id)
315 record_mock._data
316 self.mocker.result({})
317 record_mock._detached
318 self.mocker.result(['to_delete'])
319 self.server_class.delete_attachment({}, 'to_delete')
320 record_mock.list_attachments()
321 self.mocker.result(['to_add'])
322 record_mock.attachment_data('to_add')
323 self.mocker.result(('data', 'type'))
324 self.server_class.put_attachment({}, 'data', 'to_add', 'type')
325 self.server_class[record_id] = {}
326 self.mocker.replay()
327 create_database(self)
328 self.assertEqual(record_id, self.database.put_record(record_mock))
329
330 def test_put_records_batch(self):
331 """Test putting a batch of records."""
332 records = {'1': (self.mocker.mock(), {'name': '1'}),
333 '2': (self.mocker.mock(), {'name': '2'})}
334 put_result = []
335 for record_id in records:
336 put_result.append((True, record_id, record_id))
337 set_database_expectations(self)
338 for record_id in records:
339 records[record_id][0].record_id
340 self.mocker.result(record_id)
341 # TODO: we currently call _data twice, we should get
342 # this number down
343 records[record_id][0]._data
344 self.mocker.result(records[record_id][1])
345 records[record_id][0]._data
346 self.mocker.result(records[record_id][1])
347 records[record_id][0]._detached
348 self.mocker.result(['to_delete'])
349 self.server_class.delete_attachment(
350 records[record_id][1], 'to_delete')
351 records[record_id][0].list_attachments()
352 self.mocker.result(['to_add'])
353 records[record_id][0].attachment_data('to_add')
354 self.mocker.result(('data', 'type'))
355 self.server_class.put_attachment({'_id': record_id,
356 '_rev': record_id}, 'data', 'to_add', 'type')
357 self.server_class.update([records[record_id][1]
358 for record_id in records])
359 self.mocker.result(put_result)
360 self.mocker.replay()
361 create_database(self)
362 self.assertEqual(put_result, self.database.put_records_batch(
363 [records[record_id][0] for record_id in records]))
364
365 def test_delete_record_no_attachments(self):
366 """Test deleting a record with no attachments."""
367 # replace the base_n funtion which is used to
368 # return the string representation of the uuid
369 base_n = self.mocker.replace(
370 'desktopcouch.records.database.base_n')
371 # replace deepcopy because it is used to create a duplicate of
372 # the record
373 deepcopy = self.mocker.replace('copy.deepcopy')
374 record_id = 'test_record'
375 data = {record_id: record_id}
376 record_mock = self.mocker.mock()
377 trash_mock = self.mocker.mock()
378 set_database_expectations(self)
379 self.server_class[record_id]
380 self.mocker.result(data)
381 self.record_factory(data=data)
382 self.mocker.result(record_mock)
383 deepcopy(record_mock)
384 self.mocker.result(record_mock)
385 base_n(ANY, ANY)
386 self.mocker.result(record_id)
387 record_mock.record_id = record_id
388 del record_mock._data['_rev']
389 del record_mock._data['_attachments']
390 record_mock.application_annotations['desktopcouch'] = ANY
391 del self.server_class[record_id]
392 trash_mock.put_record(record_mock)
393 self.mocker.replay()
394 create_database(self)
395 self.database._dctrash = trash_mock
396 self.database.delete_record(record_id)
397
398 def test_record_exists(self):
399 """Test checking whether a record exists."""
400 record_id = 'test_record'
401 set_database_expectations(self)
402 record_id in self.server_class
403 self.mocker.result(True)
404 record_id in self.server_class
405 self.mocker.result(False)
406 self.mocker.replay()
407 create_database(self)
408 self.assertTrue(self.database.record_exists(record_id))
409 self.assertFalse(self.database.record_exists(record_id))
410
411 def test_update_fields(self):
412 """Test the update_fields method."""
413 dictionary = {'record_number': 0, 'field1': 1, 'field2': 2}
414 record_id = 'test_record'
415 set_database_expectations(self)
416 self.server_class[record_id]
417 self.mocker.result(dictionary)
418 self.server_class[record_id] = {'record_number': 0, 'field1': 11,
419 'field2': 2}
420 self.mocker.replay()
421 create_database(self)
422 # no assertions are required because mocker will make sure that
423 # the new saved dict matches the expected one.
424 self.database.update_fields(record_id, {'field1': 11})
425
426 def test_delete_not_present_view(self):
427 """Test deleting a view that is not in the db."""
428 view_name = 'my_view'
429 design_doc = 'my_design'
430 doc_id = "_design/%s" % design_doc
431 set_database_expectations(self)
432 self.server_class[doc_id]['views']
433 self.mocker.throw(ResourceNotFound)
434 self.mocker.replay()
435 create_database(self)
436 self.assertRaises(KeyError, self.database.delete_view, view_name,
437 design_doc)
438
439 def test_delete_present_view_not_last(self):
440 """Test deleting a view that is not the last in the doc."""
441 view_name = 'my_view'
442 design_doc = 'my_design'
443 doc_id = "_design/%s" % design_doc
444 container_mock = self.mocker.mock()
445 key_mock = self.mocker.mock()
446 value_mock = self.mocker.mock()
447 iteritems = [(key_mock, value_mock)]
448 set_database_expectations(self)
449 self.server_class[doc_id]['views']
450 self.mocker.result(container_mock)
451 container_mock.pop(view_name)
452 self.mocker.result(view_name)
453 len(container_mock)
454 self.mocker.result(3)
455 container_mock.iteritems()
456 self.mocker.result(iteritems)
457 value_mock.get('map')
458 self.mocker.result('map')
459 value_mock.get('reduce')
460 self.mocker.result('reduce')
461 self.views_factory(design_doc, key_mock, 'map', 'reduce')
462 self.mocker.result(container_mock)
463 self.views_factory.sync_many(ANY, ANY, remove_missing=True)
464 # set expectations for the view_exists operation
465 self.server_class[ANY]['views']
466 self.mocker.result(self.server_class)
467 ANY in self.server_class
468 self.mocker.result(False)
469 self.mocker.replay()
470 create_database(self)
471 self.assertEqual(view_name, self.database.delete_view(
472 view_name, design_doc))
473
474 def test_delete_present_view_is_last(self):
475 """Test deleting a view that is the last of the doc."""
476 view_name = 'my_view'
477 design_doc = 'my_design'
478 doc_id = "_design/%s" % design_doc
479 container_mock = self.mocker.mock()
480 set_database_expectations(self)
481 self.server_class[doc_id]['views']
482 self.mocker.result(container_mock)
483 container_mock.pop(view_name)
484 self.mocker.result(view_name)
485 len(container_mock)
486 self.mocker.result(0)
487 del self.server_class[doc_id]
488 # set expectations for the view_exists operation
489 self.server_class[ANY]['views']
490 self.mocker.result(self.server_class)
491 ANY in self.server_class
492 self.mocker.result(False)
493 self.mocker.replay()
494 create_database(self)
495 self.assertEqual(view_name, self.database.delete_view(
496 view_name, design_doc))
497
498 def test_list_views_no_design_doc(self):
499 """Test the list views when they do not exist.."""
500 design_doc = 'my_design'
501 doc_id = "_design/%s" % design_doc
502 set_database_expectations(self)
503 self.server_class[doc_id]['views']
504 self.mocker.throw(KeyError)
505 self.mocker.replay()
506 create_database(self)
507 self.assertEqual([], self.database.list_views(design_doc))
508
509 def test_list_views(self):
510 """Test the list views."""
511 design_doc = 'my_design'
512 doc_id = "_design/%s" % design_doc
513 views = ['one', 'two', 'three']
514 set_database_expectations(self)
515 self.server_class[doc_id]['views']
516 self.mocker.result(views)
517 self.mocker.replay()
518 create_database(self)
519 self.assertEqual(views, self.database.list_views(design_doc))
0520
=== modified file 'desktopcouch/records/tests/test_server.py'
--- desktopcouch/records/tests/test_server.py 2011-01-03 15:02:36 +0000
+++ desktopcouch/records/tests/test_server.py 2011-01-05 17:11:33 +0000
@@ -106,7 +106,7 @@
106 break106 break
107107
108 def test_get_records_by_record_type_save_view(self):108 def test_get_records_by_record_type_save_view(self):
109 """Test getting mutliple records by type"""109 """Test getting multiple records by type"""
110 records = self.database.get_records(110 records = self.database.get_records(
111 record_type="test.com", create_view=True)111 record_type="test.com", create_view=True)
112 self.maybe_die() # should be able to survive couchdb death112 self.maybe_die() # should be able to survive couchdb death

Subscribers

People subscribed via source and target branches