20
20
"""testing database/contact.py module"""
26
23
import desktopcouch.tests as test_environment
27
24
from desktopcouch.records.server import CouchDatabase
28
25
from desktopcouch.records.server_base import (
29
row_is_deleted, NoSuchDatabase, FieldsConflict, ResourceConflict)
26
row_is_deleted, NoSuchDatabase, FieldsConflict)
30
27
from desktopcouch.records.record import Record
31
from desktopcouch.stop_local_couchdb import stop_couchdb
32
from desktopcouch import find_pid
34
29
# pylint can't deal with failing imports even when they're handled
35
30
# pylint: disable-msg=F0401
58
53
# Connect to CouchDB server
59
54
self.dbname = self._testMethodName
60
55
self.database = CouchDatabase(self.dbname, create=True,
61
ctx=self.get_test_context())
56
ctx=test_environment.test_context)
62
57
#create some records to pull out and test
63
58
self.database.put_record(Record({
64
59
"key1_1": "val1_1", "key1_2": "val1_2", "key1_3": "val1_3",
73
68
def tearDown(self):
74
69
"""tear down each test"""
75
this_context = self.get_test_context()
76
if this_context != test_environment.test_context:
77
stop_couchdb(ctx=this_context)
78
70
super(TestCouchDatabase, self).tearDown()
80
def get_test_context(self):
81
return test_environment.test_context
86
def wait_until_server_dead(self, pid=None):
88
pid = find_pid(start_if_not_running=False, ctx=self.get_test_context())
93
os.kill(pid, 0) # Wait until exited
71
del self.database._server[self.dbname]
98
73
def test_database_not_exists(self):
103
78
"""Test getting mutliple records by type"""
104
79
records = self.database.get_records(
105
80
record_type="test.com",create_view=True)
106
self.maybe_die() # should be able to survive couchdb death
107
self.assertEqual(3, len(records))
81
self.assertEqual(3,len(records))
109
83
def test_get_record(self):
110
84
"""Test getting a record."""
111
85
record = Record({'record_number': 0}, record_type="http://example.com/")
112
86
record_id = self.database.put_record(record)
113
self.maybe_die() # should be able to survive couchdb death
114
87
retrieved_record = self.database.get_record(record_id)
115
88
self.assertEqual(0, retrieved_record['record_number'])
118
91
"""Test putting a record."""
119
92
record = Record({'record_number': 0}, record_type="http://example.com/")
120
93
record_id = self.database.put_record(record)
121
self.maybe_die() # should be able to survive couchdb death
122
retrieved_record = self.database.get_record(record_id)
94
retrieved_record = self.database._server[self.dbname][record_id]
124
96
record['record_number'], retrieved_record['record_number'])
149
121
record = Record({'record_number': 0}, record_type="http://example.com/")
150
122
record_id = self.database.put_record(record)
151
123
self.database.delete_record(record_id)
152
###self.maybe_die() # should be able to survive couchdb death
153
124
deleted_record = self.database._server[self.dbname][record_id]
154
#deleted_record = self.database.get_record(record_id)
155
125
self.assert_(deleted_record['application_annotations']['Ubuntu One'][
156
126
'private_application_annotations']['deleted'])
158
def test_delete_and_recreate_record(self):
159
"""Test deletion of records."""
160
old_record = Record({'record_number': 0},
161
record_type="http://example.com/", record_id="Reuse me")
162
old_record_id = self.database.put_record(old_record)
163
self.database.delete_record(old_record_id)
165
new_record = Record({'something else': 42},
166
record_type="http://example.com/", record_id=old_record_id)
167
new_record_id = self.database.put_record(new_record)
169
self.assertEqual(old_record_id, new_record_id)
171
new_record_fetched = self.database.get_record(new_record_id)
172
self.assertEqual(new_record_fetched["something else"], 42)
174
# this still fails, to be sure.
175
bad_record = Record({'again': 'prev not deleted'},
176
record_type="http://example.com/", record_id=old_record_id)
177
self.assertRaises(ResourceConflict,
178
self.database.put_record, bad_record)
180
self.database.delete_record(new_record_id)
182
128
def test_get_deleted_record(self):
183
129
"""Test (not) getting a deleted record."""
184
130
record = Record({'record_number': 0}, record_type="http://example.com/")
185
131
record_id = self.database.put_record(record)
186
132
self.database.delete_record(record_id)
187
self.maybe_die() # should be able to survive couchdb death
188
133
retrieved_record = self.database.get_record(record_id)
189
134
self.assertEqual(None, retrieved_record)
193
138
record = Record({'record_number': 0}, record_type="http://example.com/")
194
139
self.assert_(not self.database.record_exists("ThisMustNotExist"))
195
140
record_id = self.database.put_record(record)
196
self.maybe_die() # should be able to survive couchdb death
197
141
self.assert_(self.database.record_exists(record_id))
198
142
self.database.delete_record(record_id)
199
143
self.assert_(not self.database.record_exists(record_id))
203
147
dictionary = {'record_number': 0, 'field1': 1, 'field2': 2}
204
148
record = Record(dictionary, record_type="http://example.com/")
205
149
record_id = self.database.put_record(record)
206
self.maybe_die() # should be able to survive couchdb death
207
150
# manipulate the database 'out of view'
208
151
non_working_copy = self.database.get_record(record_id)
209
152
non_working_copy['field2'] = 22
210
153
non_working_copy['field3'] = 3
211
154
self.database.put_record(non_working_copy)
212
155
self.database.update_fields(record_id, {'field1': 11})
213
self.maybe_die() # should be able to survive couchdb death
214
156
working_copy = self.database.get_record(record_id)
215
157
self.assertEqual(0, working_copy['record_number'])
216
158
self.assertEqual(11, working_copy['field1'])
234
176
self.assertRaises(
235
177
KeyError, self.database.delete_view, view2_name, design_doc)
236
178
self.database.add_view(view1_name, map_js, reduce_js, design_doc)
237
self.maybe_die() # should be able to survive couchdb death
238
179
self.database.add_view(view2_name, map_js, reduce_js, design_doc)
239
180
self.database.delete_view(view1_name, design_doc)
240
181
self.assertRaises(
241
182
KeyError, self.database.delete_view, view1_name, design_doc)
242
self.maybe_die() # should be able to survive couchdb death
243
183
self.database.delete_view(view2_name, design_doc)
244
184
self.assertRaises(
245
185
KeyError, self.database.delete_view, view2_name, design_doc)
269
209
results = self.database.get_records(create_view=True)
271
self.assertTrue(8 <= len(results.rows)) # our 8, plus callers' data
272
self.assertIn("record_type", results.rows[0].value)
274
211
for row in results[good_record_type]: # index notation
275
212
self.assertTrue(row.id in record_ids_we_care_about)
276
213
record_ids_we_care_about.remove(row.id)
277
214
self.assertFalse(row_is_deleted(row))
279
self.maybe_die() # should be able to survive couchdb death
280
216
self.assertTrue(len(record_ids_we_care_about) == 0, "expected zero")
282
218
self.assertRaises(KeyError, self.database.get_records,
290
226
map_js = """function(doc) { emit(doc._id, null) }"""
291
227
self.database.add_view(view_name, map_js, None, design_doc)
293
self.maybe_die() # should be able to survive couchdb death
294
229
self.assertEqual(self.database.list_views(design_doc), [view_name])
295
230
self.database.delete_view(view_name, design_doc)
299
234
def test_get_view_by_type_new_but_already(self):
300
235
self.database.get_records(create_view=True)
301
self.maybe_die() # should be able to survive couchdb death
302
236
self.database.get_records(create_view=True)
303
237
# No exceptions on second run? Yay.
305
239
def test_get_view_by_type_createxcl_fail(self):
306
240
self.database.get_records(create_view=True)
307
self.maybe_die() # should be able to survive couchdb death
308
241
self.assertRaises(KeyError, self.database.get_records, create_view=None)
310
243
def test_get_changes(self):
364
297
# Ensure time is same.
365
298
self.assertEqual(saved_time, self.database._changes_last_used)
367
###self.maybe_die() # should be able to survive couchdb death
369
300
# Next time we run, we get the same event again.
370
301
# Consume queued changes.
371
302
count = self.database.report_changes(rep)
374
305
# Ensure position different.
375
306
self.assertEqual(saved_position + 1, self.database._changes_since)
377
def test_view_wrapper_has_all_attributes(self):
378
design_doc = "test_view_wrapper_has_all_attributes"
379
view1_name = "unit_tests_are_great_yeah"
381
map_js = """function(doc) { emit(doc._id, null) }"""
383
self.database.add_view(view1_name, map_js, None, design_doc)
384
view = self.database.execute_view(view1_name, design_doc)
386
for attr_name in dir(view.obj):
387
# This works because hasattr actually calls getattr.
388
self.assertTrue(hasattr(view, attr_name))
390
308
def test_report_changes_all_ops_give_known_keys(self):
391
309
def rep(**kwargs):
392
310
self.failUnless("changes" in kwargs)
428
346
constructed_record.attach(content, "nu/mbe/rs", "text/plain")
429
347
constructed_record.attach("string", "another document", "text/plain")
431
###self.maybe_die() # should be able to survive couchdb death
432
constructed_record.attach("XXXXXXXXX", "never used", "text/plain")
433
constructed_record.detach("never used") # detach works before commit.
435
349
# We can read from a document that we constructed.
436
350
out_file, out_content_type = \
437
351
constructed_record.attachment_data("nu/mbe/rs")
441
355
self.assertRaises(KeyError, constructed_record.attach, content,
442
356
"another document", "text/x-rst")
444
###self.maybe_die() # should be able to survive couchdb death
445
358
record_id = self.database.put_record(constructed_record)
446
359
retrieved_record = self.database.get_record(record_id)
453
366
retrieved_record = self.database.get_record(record_id)
455
# Remove and test that it is indeed gone.
368
# To replace, one must remove first.
456
369
retrieved_record.detach("another document")
457
self.assertRaises(KeyError, retrieved_record.detach,
461
record_id = self.database.put_record(retrieved_record)
463
retrieved_record = self.database.get_record(record_id)
465
###self.maybe_die() # should be able to survive couchdb death
466
# We can get a list of attachments.
467
self.assertEqual(set(retrieved_record.list_attachments()),
468
set(["nu/mbe/rs", "Document"]))
471
370
retrieved_record.attach(content, "another document", "text/plain")
473
372
# push new version
485
384
self.assertEqual(out_data, content.getvalue())
486
385
self.assertEqual(out_content_type, "text/plain")
488
###self.maybe_die() # should be able to survive couchdb death
489
387
# Asking for a named document that does not exist causes KeyError.
490
388
self.assertRaises(KeyError, retrieved_record.attachment_data,
516
414
# ordinary requests are in key order
517
415
self.assertEqual(data, sorted(data))
519
self.maybe_die() # should be able to survive couchdb death
520
417
# now request descending order and confirm that it *is* descending
521
418
descdata = [i.key for i in
522
419
list(self.database.execute_view(view1_name, design_doc,
577
474
except FieldsConflict, e:
578
475
self.assertEqual({('field1',): (22, 11)}, e.conflicts)
581
class TestServerDiesSegv(TestCouchDatabase):
582
def get_test_context(self):
585
except AttributeError:
586
self.ctx = test_environment.create_new_test_environment()
590
self.database.ensure_full_commit()
592
pid = find_pid(start_if_not_running=False, ctx=self.get_test_context())
594
print "couchdb has already quit! That's unexpected."
596
print "DIE, process", pid, "!"
597
os.kill(pid, signal.SIGSEGV)
598
self.wait_until_server_dead(pid=pid)
601
class TestServerDiesNormal(TestCouchDatabase):
602
def get_test_context(self):
605
except AttributeError:
606
self.ctx = test_environment.create_new_test_environment()
610
self.database.ensure_full_commit()
612
stop_couchdb(ctx=self.get_test_context())
613
self.wait_until_server_dead()