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
1=== modified file 'desktopcouch/application/server.py'
2--- desktopcouch/application/server.py 2010-11-26 20:30:30 +0000
3+++ desktopcouch/application/server.py 2011-01-05 17:11:33 +0000
4@@ -64,7 +64,7 @@
5
6 def __init__(self, database, uri=None, record_factory=None,
7 create=False, server_class=DesktopServer, oauth_tokens=None,
8- ctx=None):
9+ ctx=None, views_factory=None):
10 if ctx is None:
11 ctx = DEFAULT_CONTEXT
12 self.ctx = ctx
13@@ -72,7 +72,7 @@
14 super(DesktopDatabase, self).__init__(
15 database, uri, record_factory=record_factory,
16 create=create, server_class=server_class,
17- oauth_tokens=oauth_tokens, ctx=ctx)
18+ oauth_tokens=oauth_tokens, ctx=ctx, views_factory=views_factory)
19
20 # pylint: disable=W0221
21 # Arguments number differs from overridden method
22
23=== modified file 'desktopcouch/records/database.py'
24--- desktopcouch/records/database.py 2011-01-05 13:48:31 +0000
25+++ desktopcouch/records/database.py 2011-01-05 17:11:33 +0000
26@@ -26,7 +26,6 @@
27 # pylint: disable=W0402
28 import string
29 # pylint: enable=W0402
30-import uuid
31 import warnings
32
33 from time import time
34@@ -106,7 +105,12 @@
35 """A desktopcouch.records specific CouchDb database."""
36
37 def __init__(self, database, uri, record_factory=None, create=False,
38- server_class=Server, **server_class_extras):
39+ views_factory=None, server_class=Server,
40+ **server_class_extras):
41+ if views_factory is None:
42+ self._views_factory = ViewDefinition
43+ else:
44+ self._views_factory = views_factory
45 self.server_uri = uri
46 self._database_name = database
47 self.record_factory = record_factory or Record
48@@ -115,6 +119,7 @@
49 self._server_class_extras = server_class_extras
50 self._server = None
51 self.db = None
52+ self._dctrash = None
53 self._reconnect()
54 self._changes_since = self.db.info()["update_seq"]
55 self._changes_last_used = 0 # Immediate run works.
56@@ -182,40 +187,45 @@
57
58 def put_record(self, record):
59 """Put a record in back end storage."""
60- if not record.record_id:
61+ record_id = record.record_id
62+ record_data = record._data # pylint: disable=W0212
63+ if not record_id:
64 # Do not rely on couchdb to create an ID for us.
65 # pylint: disable=E1101
66- record.record_id = base_n(uuid4().int, 62)
67+ record_id = base_n(uuid4().int, 62)
68+ record.record_id = record_id
69 # pylint: enable=E1101
70- self.db[record.record_id] = record._data # pylint: disable=W0212
71+ self.db[record_id] = record_data
72
73 # pylint: disable=W0212
74 for attachment_name in getattr(record, "_detached", []):
75- self.db.delete_attachment(record._data, attachment_name)
76+ self.db.delete_attachment(record_data, attachment_name)
77
78 for attachment_name in record.list_attachments():
79 data, content_type = record.attachment_data(attachment_name)
80- self.db.put_attachment(record._data,
81+ self.db.put_attachment(record_data,
82 data,
83 attachment_name,
84 content_type)
85 # pylint: enable=W0212
86
87- return record.record_id
88+ return record_id
89
90 def put_records_batch(self, batch):
91 """Put a batch of records in back end storage."""
92 # used to access fast the record
93 records_hash = {}
94 for record in batch:
95- if not record.record_id:
96+ record_id = record.record_id
97+ if not record_id:
98 # Do not rely on couchdb to create an ID for us.
99 # pylint: disable=E1101
100- record.record_id = base_n(uuid4().int, 62)
101+ record_id = base_n(uuid4().int, 62)
102+ record.record_id = record_id
103 # pylint: enable=E1101
104- records_hash[record.record_id] = record
105+ records_hash[record_id] = record
106 # although with a single record we need to test for the
107- # revisison, with a batch we do not, but we have to make sure
108+ # revision, with a batch we do not, but we have to make sure
109 # that we did not get an error
110 # pylint: disable=W0212
111 batch_put_result = self.db.update([record._data for record in batch])
112@@ -223,15 +233,16 @@
113 success, docid, rev_or_exc = current_tuple
114 if success:
115 record = records_hash[docid]
116+ record_data = record._data
117 # set the new rev
118- record._data["_rev"] = rev_or_exc
119+ record_data["_rev"] = rev_or_exc
120 for attachment_name in getattr(record, "_detached", []):
121- self.db.delete_attachment(record._data, attachment_name)
122+ self.db.delete_attachment(record_data, attachment_name)
123 for attachment_name in record.list_attachments():
124 data, content_type = record.attachment_data(
125 attachment_name)
126 self.db.put_attachment(
127- {"_id": record.record_id, "_rev": record["_rev"]},
128+ {"_id": docid, "_rev": rev_or_exc},
129 data, attachment_name, content_type)
130 # pylint: enable=W0212
131 # all success record have the blobs added we return result of
132@@ -323,12 +334,15 @@
133 """Delete record with given id"""
134 record = self.get_record(record_id)
135 new_record = copy.deepcopy(record)
136- dctrash = self.__class__(
137- database=DCTRASH,
138- uri=self.server_uri,
139- create=True,
140- **self._server_class_extras)
141- new_record.record_id = str(uuid.uuid4())
142+ if not self._dctrash:
143+ self._dctrash = self.__class__(
144+ database=DCTRASH,
145+ uri=self.server_uri,
146+ create=True,
147+ **self._server_class_extras)
148+ # pylint: disable=E1101
149+ new_record.record_id = base_n(uuid4().int, 62)
150+ # pylint: enable=E1101
151 del new_record._data['_rev']
152 try:
153 del new_record._data['_attachments']
154@@ -339,7 +353,7 @@
155 {'original_database_name': self._database_name,
156 'original_id': record_id}}
157 del self.db[record_id]
158- return dctrash.put_record(new_record)
159+ return self._dctrash.put_record(new_record)
160 # pylint: enable=W0212
161
162 def delete_view(self, view_name, design_doc=DEFAULT_DESIGN_DOCUMENT):
163@@ -362,7 +376,7 @@
164 if len(view_container) > 0:
165 # Construct a new list of objects representing all views to have.
166 views = [
167- ViewDefinition(design_doc, k, v.get("map"),
168+ self._views_factory(design_doc, k, v.get("map"),
169 v.get("reduce"))
170 for k, v in view_container.iteritems()]
171 # Push back a new batch of view. Pray to Eris that this doesn't
172@@ -372,7 +386,7 @@
173 # its design-document from the ViewDefinition items, and if there
174 # are no items, then it has no idea of a design document to
175 # update. This is a serious flaw. Thus, the "else" to follow.
176- ViewDefinition.sync_many(self.db, views, remove_missing=True)
177+ self._views_factory.sync_many(self.db, views, remove_missing=True)
178 else:
179 # There are no views left in this design document.
180
181@@ -401,7 +415,7 @@
182 if design_doc is None:
183 design_doc = view_name
184
185- view = ViewDefinition(design_doc, view_name, map_js, reduce_js)
186+ view = self._views_factory(design_doc, view_name, map_js, reduce_js)
187 view.sync(self.db)
188 assert self.view_exists(view_name, design_doc)
189
190
191=== added file 'desktopcouch/records/tests/test_mocked_server.py'
192--- desktopcouch/records/tests/test_mocked_server.py 1970-01-01 00:00:00 +0000
193+++ desktopcouch/records/tests/test_mocked_server.py 2011-01-05 17:11:33 +0000
194@@ -0,0 +1,519 @@
195+# Copyright 2009-2010 Canonical Ltd.
196+#
197+# This file is part of desktopcouch.
198+#
199+# desktopcouch is free software: you can redistribute it and/or modify
200+# it under the terms of the GNU Lesser General Public License version 3
201+# as published by the Free Software Foundation.
202+#
203+# desktopcouch is distributed in the hope that it will be useful,
204+# but WITHOUT ANY WARRANTY; without even the implied warranty of
205+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
206+# GNU Lesser General Public License for more details.
207+#
208+# You should have received a copy of the GNU Lesser General Public License
209+# along with desktopcouch. If not, see <http://www.gnu.org/licenses/>.
210+#
211+# Authors: Manuel de la Pena<manuel@canonical.com>
212+"""Test the database code mocking the db access."""
213+
214+from couchdb.http import ResourceNotFound
215+from mocker import MockerTestCase, ANY, KWARGS
216+
217+from desktopcouch.application.server import DesktopDatabase
218+from desktopcouch.records.database import NoSuchDatabase
219+
220+
221+# disable pylint warnings that are common due to the nature of the tests
222+# pylint: disable=W0104, W0201, W0212
223+def set_database_expectations(testcase):
224+ """Set the expectations for the db creation."""
225+ testcase.server_class(testcase.uri, ctx=testcase.ctx,
226+ oauth_tokens=testcase.oauth_tokens)
227+ testcase.mocker.result(testcase.server_class)
228+ testcase.dbname in testcase.server_class
229+ testcase.mocker.result(True)
230+ testcase.server_class[testcase.dbname]
231+ testcase.mocker.result(testcase.server_class)
232+ testcase.server_class.info()
233+ testcase.mocker.result({'update_seq': []})
234+
235+
236+def set_create_view_expectations(testcase):
237+ """Set the expectations for when a view is created."""
238+ # set expectations for the creation of the view
239+ testcase.views_factory(ANY, ANY, ANY, ANY)
240+ testcase.mocker.result(testcase.views_factory)
241+ testcase.views_factory.sync(testcase.server_class)
242+ # assert that is was added, shall we be mocking this?
243+ testcase.server_class[ANY]['views']
244+ testcase.mocker.result(testcase.server_class)
245+ ANY in testcase.server_class
246+ testcase.mocker.result(True)
247+
248+
249+def create_database(testcase):
250+ """Create a database to be used in the testcase."""
251+ testcase.database = DesktopDatabase(testcase.dbname, uri=testcase.uri,
252+ record_factory=testcase.record_factory, create=True,
253+ server_class=testcase.server_class,
254+ oauth_tokens=testcase.oauth_tokens, ctx=testcase.ctx,
255+ views_factory=testcase.views_factory)
256+
257+
258+class TestMockedCouchDatabaseDeprecated(MockerTestCase):
259+ """Test the deprecated API."""
260+
261+ def setUp(self):
262+ """Setup each test."""
263+ super(TestMockedCouchDatabaseDeprecated, self).setUp()
264+ # set mocked objects
265+ self.record_factory = self.mocker.mock()
266+ self.server_class = self.mocker.mock()
267+ self.oauth_tokens = self.mocker.mock()
268+ self.ctx = self.mocker.mock()
269+ self.views_factory = self.mocker.mock()
270+ # set not mocked required data
271+ self.uri = 'uri'
272+ self.dbname = self._testMethodName
273+ self.database = None
274+
275+ def set_database_expectations(self):
276+ """Set the expectations for the creation of the db."""
277+ self.server_class(self.uri, ctx=self.ctx,
278+ oauth_tokens=self.oauth_tokens)
279+ self.mocker.result(self.server_class)
280+ self.dbname in self.server_class
281+ self.mocker.result(True)
282+ self.server_class[self.dbname]
283+ self.mocker.result(self.server_class)
284+ self.server_class.info()
285+ self.mocker.result({'update_seq': []})
286+
287+ def test_get_records_by_record_type_saved_view(self):
288+ """Test getting multiple records by type."""
289+ record_type = 'test.com'
290+ set_database_expectations(self)
291+ # set expectations for the view_exists operation
292+ self.server_class[ANY]['views']
293+ self.mocker.result(self.server_class)
294+ ANY in self.server_class
295+ self.mocker.result(True)
296+ # set expectations for the execution
297+ self.server_class.view(ANY, KWARGS)
298+ self.mocker.result({record_type: record_type})
299+ self.mocker.replay()
300+ create_database(self)
301+ records = self.database.get_records(record_type=record_type,
302+ create_view=True)
303+ self.assertEqual(record_type, records)
304+
305+ def test_get_records_by_record_type_new_view(self):
306+ """Test getting multiple record and create the view."""
307+ record_type = 'test.com'
308+ set_database_expectations(self)
309+ # set expectations for the view_exists operation
310+ self.server_class[ANY]['views']
311+ self.mocker.result(self.server_class)
312+ ANY in self.server_class
313+ self.mocker.result(False)
314+ set_create_view_expectations(self)
315+ # set expectations for the execution
316+ self.server_class.view(ANY, KWARGS)
317+ self.mocker.result({record_type: record_type})
318+ self.mocker.replay()
319+ create_database(self)
320+ records = self.database.get_records(record_type=record_type,
321+ create_view=True)
322+ self.assertEqual(record_type, records)
323+
324+ def test_get_records_by_record_type_new_view_not_create(self):
325+ """Test getting the records when the view does not exist."""
326+ record_type = 'test.com'
327+ set_database_expectations(self)
328+ # assert that is was added, shall we be mocking this?
329+ self.server_class[ANY]['views']
330+ self.mocker.result(self.server_class)
331+ ANY in self.server_class
332+ self.mocker.result(True)
333+ # set expectations for the execution
334+ self.server_class.view(ANY, KWARGS)
335+ self.mocker.result({record_type: record_type})
336+ self.mocker.replay()
337+ create_database(self)
338+ self.assertRaises(KeyError, self.database.get_records,
339+ (record_type, False))
340+
341+ def test_get_records_by_record_type_saved_view_false_create(self):
342+ """Test calling the view and do not create it."""
343+ record_type = 'test.com'
344+ set_database_expectations(self)
345+ # set expectations for the view_exists operation
346+ self.server_class[ANY]['views']
347+ self.mocker.result(self.server_class)
348+ ANY in self.server_class
349+ self.mocker.result(True)
350+ # set expectations for the execution
351+ self.server_class.view(ANY, KWARGS)
352+ self.mocker.result({record_type: record_type})
353+ self.mocker.replay()
354+ create_database(self)
355+ records = self.database.get_records(record_type=record_type,
356+ create_view=False)
357+ self.assertEqual(record_type, records)
358+
359+
360+class TestMockedDesktopDatabase(MockerTestCase):
361+ """Test case for DesktopDatabase."""
362+
363+ def setUp(self):
364+ """setup each test"""
365+ super(TestMockedDesktopDatabase, self).setUp()
366+ # set mocked objects
367+ self.record_factory = self.mocker.mock()
368+ self.server_class = self.mocker.mock()
369+ self.oauth_tokens = self.mocker.mock()
370+ self.ctx = self.mocker.mock()
371+ self.views_factory = self.mocker.mock()
372+ # set not mocked required data
373+ self.uri = 'uri'
374+ self.dbname = self._testMethodName
375+ self.database = None
376+
377+ def _set_get_all_records_expectations(self, record_type, view_result,
378+ records):
379+ """Set the common expectations for the get_all_records call."""
380+ # replace the transform to records funtion which is used to
381+ # return records instead of view results
382+ transform_to_records = self.mocker.replace(
383+ 'desktopcouch.records.database.transform_to_records')
384+ self.server_class.view(ANY, KWARGS)
385+ self.mocker.result(view_result)
386+ transform_to_records(record_type)
387+ self.mocker.result(records)
388+
389+ def test_database_not_exists(self):
390+ """Test that the database does not exist."""
391+ # set expectations so that the server class returns that
392+ # the db does ot exist.
393+ self.server_class(self.uri, ctx=self.ctx,
394+ oauth_tokens=self.oauth_tokens)
395+ self.mocker.result(self.server_class)
396+ self.dbname in self.server_class
397+ self.mocker.result(False)
398+ self.mocker.replay()
399+ self.assertRaises(NoSuchDatabase, DesktopDatabase, self.dbname,
400+ uri=self.uri, record_factory=self.record_factory, create=False,
401+ server_class=self.server_class, oauth_tokens=self.oauth_tokens,
402+ ctx=self.ctx, views_factory=self.views_factory)
403+
404+ def test_get_records_by_record_type_present_view(self):
405+ """Test getting mutliple records by type with a present view."""
406+ record_type = 'test.com'
407+ view_result = {record_type: record_type}
408+ records = ['first', 'second']
409+ set_database_expectations(self)
410+ self._set_get_all_records_expectations(record_type, view_result,
411+ records)
412+ self.mocker.replay()
413+ create_database(self)
414+ self.assertEqual(records,
415+ self.database.get_all_records(record_type=record_type))
416+
417+ def test_get_records_by_record_type_missing_view(self):
418+ """Test getting mutliple records by type with a present view."""
419+ record_type = 'test.com'
420+ view_result = {record_type: record_type}
421+ records = ['first', 'second']
422+ set_database_expectations(self)
423+ self.server_class.view(ANY, KWARGS)
424+ # raise exception so that the view has to be created.
425+ self.mocker.throw(ResourceNotFound)
426+ set_create_view_expectations(self)
427+ # set the expectations to be used after the creation of the view
428+ self._set_get_all_records_expectations(record_type, view_result,
429+ records)
430+ self.mocker.replay()
431+ create_database(self)
432+ self.assertEqual(records,
433+ self.database.get_all_records(record_type=record_type))
434+
435+ def test_get_present_record_no_attachments(self):
436+ """Test getting a record."""
437+ record_id = 'test_record'
438+ data = {record_id: record_id}
439+ record_mock = self.mocker.mock()
440+ set_database_expectations(self)
441+ self.server_class[record_id]
442+ self.mocker.result(data)
443+ self.record_factory(data=data)
444+ self.mocker.result(record_mock)
445+ self.mocker.replay()
446+ create_database(self)
447+ self.assertEqual(record_mock, self.database.get_record(record_id))
448+
449+ def test_get_deleted_record(self):
450+ """Test getting a deleted record."""
451+ record_id = 'random_id'
452+ set_database_expectations(self)
453+ self.server_class[record_id]
454+ self.mocker.throw(ResourceNotFound)
455+ self.mocker.replay()
456+ create_database(self)
457+ self.assertEqual(None, self.database.get_record(record_id))
458+
459+ def test_put_record_no_attachments_or_id(self):
460+ """Test putting a record with no attachments."""
461+ record_id = 'random_id'
462+ record_mock = self.mocker.mock()
463+ # replace the base_n funtion which is used to
464+ # return the string representation of the uuid
465+ base_n = self.mocker.replace(
466+ 'desktopcouch.records.database.base_n')
467+ set_database_expectations(self)
468+ record_mock.record_id
469+ self.mocker.result(None)
470+ base_n(ANY, ANY)
471+ self.mocker.result(record_id)
472+ record_mock.record_id = record_id
473+ record_mock._detached
474+ self.mocker.result([])
475+ record_mock.list_attachments()
476+ self.mocker.result([])
477+ record_mock._data
478+ self.mocker.result({})
479+ self.server_class[record_id] = {}
480+ self.mocker.replay()
481+ create_database(self)
482+ self.assertEqual(record_id, self.database.put_record(record_mock))
483+
484+ def test_put_record_no_attachments(self):
485+ """Test putting a record with no attachments."""
486+ record_id = 'random_id'
487+ record_mock = self.mocker.mock()
488+ set_database_expectations(self)
489+ record_mock.record_id
490+ self.mocker.result(record_id)
491+ record_mock._detached
492+ self.mocker.result([])
493+ record_mock.list_attachments()
494+ self.mocker.result([])
495+ record_mock._data
496+ self.mocker.result({})
497+ self.server_class[record_id] = {}
498+ self.mocker.replay()
499+ create_database(self)
500+ self.assertEqual(record_id, self.database.put_record(record_mock))
501+
502+ def test_put_record_with_attachments(self):
503+ """Test putting a record with attachments."""
504+ record_id = 'random_id'
505+ record_mock = self.mocker.mock()
506+ set_database_expectations(self)
507+ record_mock.record_id
508+ self.mocker.result(record_id)
509+ record_mock._data
510+ self.mocker.result({})
511+ record_mock._detached
512+ self.mocker.result(['to_delete'])
513+ self.server_class.delete_attachment({}, 'to_delete')
514+ record_mock.list_attachments()
515+ self.mocker.result(['to_add'])
516+ record_mock.attachment_data('to_add')
517+ self.mocker.result(('data', 'type'))
518+ self.server_class.put_attachment({}, 'data', 'to_add', 'type')
519+ self.server_class[record_id] = {}
520+ self.mocker.replay()
521+ create_database(self)
522+ self.assertEqual(record_id, self.database.put_record(record_mock))
523+
524+ def test_put_records_batch(self):
525+ """Test putting a batch of records."""
526+ records = {'1': (self.mocker.mock(), {'name': '1'}),
527+ '2': (self.mocker.mock(), {'name': '2'})}
528+ put_result = []
529+ for record_id in records:
530+ put_result.append((True, record_id, record_id))
531+ set_database_expectations(self)
532+ for record_id in records:
533+ records[record_id][0].record_id
534+ self.mocker.result(record_id)
535+ # TODO: we currently call _data twice, we should get
536+ # this number down
537+ records[record_id][0]._data
538+ self.mocker.result(records[record_id][1])
539+ records[record_id][0]._data
540+ self.mocker.result(records[record_id][1])
541+ records[record_id][0]._detached
542+ self.mocker.result(['to_delete'])
543+ self.server_class.delete_attachment(
544+ records[record_id][1], 'to_delete')
545+ records[record_id][0].list_attachments()
546+ self.mocker.result(['to_add'])
547+ records[record_id][0].attachment_data('to_add')
548+ self.mocker.result(('data', 'type'))
549+ self.server_class.put_attachment({'_id': record_id,
550+ '_rev': record_id}, 'data', 'to_add', 'type')
551+ self.server_class.update([records[record_id][1]
552+ for record_id in records])
553+ self.mocker.result(put_result)
554+ self.mocker.replay()
555+ create_database(self)
556+ self.assertEqual(put_result, self.database.put_records_batch(
557+ [records[record_id][0] for record_id in records]))
558+
559+ def test_delete_record_no_attachments(self):
560+ """Test deleting a record with no attachments."""
561+ # replace the base_n funtion which is used to
562+ # return the string representation of the uuid
563+ base_n = self.mocker.replace(
564+ 'desktopcouch.records.database.base_n')
565+ # replace deepcopy because it is used to create a duplicate of
566+ # the record
567+ deepcopy = self.mocker.replace('copy.deepcopy')
568+ record_id = 'test_record'
569+ data = {record_id: record_id}
570+ record_mock = self.mocker.mock()
571+ trash_mock = self.mocker.mock()
572+ set_database_expectations(self)
573+ self.server_class[record_id]
574+ self.mocker.result(data)
575+ self.record_factory(data=data)
576+ self.mocker.result(record_mock)
577+ deepcopy(record_mock)
578+ self.mocker.result(record_mock)
579+ base_n(ANY, ANY)
580+ self.mocker.result(record_id)
581+ record_mock.record_id = record_id
582+ del record_mock._data['_rev']
583+ del record_mock._data['_attachments']
584+ record_mock.application_annotations['desktopcouch'] = ANY
585+ del self.server_class[record_id]
586+ trash_mock.put_record(record_mock)
587+ self.mocker.replay()
588+ create_database(self)
589+ self.database._dctrash = trash_mock
590+ self.database.delete_record(record_id)
591+
592+ def test_record_exists(self):
593+ """Test checking whether a record exists."""
594+ record_id = 'test_record'
595+ set_database_expectations(self)
596+ record_id in self.server_class
597+ self.mocker.result(True)
598+ record_id in self.server_class
599+ self.mocker.result(False)
600+ self.mocker.replay()
601+ create_database(self)
602+ self.assertTrue(self.database.record_exists(record_id))
603+ self.assertFalse(self.database.record_exists(record_id))
604+
605+ def test_update_fields(self):
606+ """Test the update_fields method."""
607+ dictionary = {'record_number': 0, 'field1': 1, 'field2': 2}
608+ record_id = 'test_record'
609+ set_database_expectations(self)
610+ self.server_class[record_id]
611+ self.mocker.result(dictionary)
612+ self.server_class[record_id] = {'record_number': 0, 'field1': 11,
613+ 'field2': 2}
614+ self.mocker.replay()
615+ create_database(self)
616+ # no assertions are required because mocker will make sure that
617+ # the new saved dict matches the expected one.
618+ self.database.update_fields(record_id, {'field1': 11})
619+
620+ def test_delete_not_present_view(self):
621+ """Test deleting a view that is not in the db."""
622+ view_name = 'my_view'
623+ design_doc = 'my_design'
624+ doc_id = "_design/%s" % design_doc
625+ set_database_expectations(self)
626+ self.server_class[doc_id]['views']
627+ self.mocker.throw(ResourceNotFound)
628+ self.mocker.replay()
629+ create_database(self)
630+ self.assertRaises(KeyError, self.database.delete_view, view_name,
631+ design_doc)
632+
633+ def test_delete_present_view_not_last(self):
634+ """Test deleting a view that is not the last in the doc."""
635+ view_name = 'my_view'
636+ design_doc = 'my_design'
637+ doc_id = "_design/%s" % design_doc
638+ container_mock = self.mocker.mock()
639+ key_mock = self.mocker.mock()
640+ value_mock = self.mocker.mock()
641+ iteritems = [(key_mock, value_mock)]
642+ set_database_expectations(self)
643+ self.server_class[doc_id]['views']
644+ self.mocker.result(container_mock)
645+ container_mock.pop(view_name)
646+ self.mocker.result(view_name)
647+ len(container_mock)
648+ self.mocker.result(3)
649+ container_mock.iteritems()
650+ self.mocker.result(iteritems)
651+ value_mock.get('map')
652+ self.mocker.result('map')
653+ value_mock.get('reduce')
654+ self.mocker.result('reduce')
655+ self.views_factory(design_doc, key_mock, 'map', 'reduce')
656+ self.mocker.result(container_mock)
657+ self.views_factory.sync_many(ANY, ANY, remove_missing=True)
658+ # set expectations for the view_exists operation
659+ self.server_class[ANY]['views']
660+ self.mocker.result(self.server_class)
661+ ANY in self.server_class
662+ self.mocker.result(False)
663+ self.mocker.replay()
664+ create_database(self)
665+ self.assertEqual(view_name, self.database.delete_view(
666+ view_name, design_doc))
667+
668+ def test_delete_present_view_is_last(self):
669+ """Test deleting a view that is the last of the doc."""
670+ view_name = 'my_view'
671+ design_doc = 'my_design'
672+ doc_id = "_design/%s" % design_doc
673+ container_mock = self.mocker.mock()
674+ set_database_expectations(self)
675+ self.server_class[doc_id]['views']
676+ self.mocker.result(container_mock)
677+ container_mock.pop(view_name)
678+ self.mocker.result(view_name)
679+ len(container_mock)
680+ self.mocker.result(0)
681+ del self.server_class[doc_id]
682+ # set expectations for the view_exists operation
683+ self.server_class[ANY]['views']
684+ self.mocker.result(self.server_class)
685+ ANY in self.server_class
686+ self.mocker.result(False)
687+ self.mocker.replay()
688+ create_database(self)
689+ self.assertEqual(view_name, self.database.delete_view(
690+ view_name, design_doc))
691+
692+ def test_list_views_no_design_doc(self):
693+ """Test the list views when they do not exist.."""
694+ design_doc = 'my_design'
695+ doc_id = "_design/%s" % design_doc
696+ set_database_expectations(self)
697+ self.server_class[doc_id]['views']
698+ self.mocker.throw(KeyError)
699+ self.mocker.replay()
700+ create_database(self)
701+ self.assertEqual([], self.database.list_views(design_doc))
702+
703+ def test_list_views(self):
704+ """Test the list views."""
705+ design_doc = 'my_design'
706+ doc_id = "_design/%s" % design_doc
707+ views = ['one', 'two', 'three']
708+ set_database_expectations(self)
709+ self.server_class[doc_id]['views']
710+ self.mocker.result(views)
711+ self.mocker.replay()
712+ create_database(self)
713+ self.assertEqual(views, self.database.list_views(design_doc))
714
715=== modified file 'desktopcouch/records/tests/test_server.py'
716--- desktopcouch/records/tests/test_server.py 2011-01-03 15:02:36 +0000
717+++ desktopcouch/records/tests/test_server.py 2011-01-05 17:11:33 +0000
718@@ -106,7 +106,7 @@
719 break
720
721 def test_get_records_by_record_type_save_view(self):
722- """Test getting mutliple records by type"""
723+ """Test getting multiple records by type"""
724 records = self.database.get_records(
725 record_type="test.com", create_view=True)
726 self.maybe_die() # should be able to survive couchdb death

Subscribers

People subscribed via source and target branches