~ubuntu-branches/ubuntu/trusty/desktopcouch/trusty

« back to all changes in this revision

Viewing changes to desktopcouch/records/tests/test_server.py

  • Committer: Bazaar Package Importer
  • Author(s): Ken VanDine
  • Date: 2010-04-19 12:52:22 UTC
  • mfrom: (12.1.9 lucid)
  • Revision ID: james.westby@ubuntu.com-20100419125222-zsh9lrm15h84951j
* debian/patches/lp_522538.patch
  - Handle reconnects if the server isn't running (LP: #522538)

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
 
20
20
"""testing database/contact.py module"""
21
21
import testtools
22
 
import os
23
 
import signal
24
 
import time
25
22
 
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
33
28
 
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",
72
67
 
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()
79
 
 
80
 
    def get_test_context(self):
81
 
        return test_environment.test_context
82
 
 
83
 
    def maybe_die(self):
84
 
        pass
85
 
 
86
 
    def wait_until_server_dead(self, pid=None):
87
 
        if pid is not None:
88
 
            pid = find_pid(start_if_not_running=False, ctx=self.get_test_context())
89
 
        if pid is None:
90
 
            return
91
 
        while True:
92
 
            try:
93
 
                os.kill(pid, 0)  # Wait until exited
94
 
                time.sleep(0.1)
95
 
            except OSError:
96
 
                break
 
71
        del self.database._server[self.dbname]
97
72
 
98
73
    def test_database_not_exists(self):
99
74
        self.assertRaises(
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))
108
82
 
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'])
116
89
 
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]
123
95
        self.assertEqual(
124
96
            record['record_number'], retrieved_record['record_number'])
125
97
 
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'])
157
127
 
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)
164
 
 
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)
168
 
 
169
 
        self.assertEqual(old_record_id, new_record_id)
170
 
 
171
 
        new_record_fetched = self.database.get_record(new_record_id)
172
 
        self.assertEqual(new_record_fetched["something else"], 42)
173
 
 
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)
179
 
 
180
 
        self.database.delete_record(new_record_id)
181
 
 
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)
190
135
 
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)
268
208
 
269
209
        results = self.database.get_records(create_view=True)
270
210
 
271
 
        self.assertTrue(8 <= len(results.rows))  # our 8, plus callers' data
272
 
        self.assertIn("record_type", results.rows[0].value)
273
 
 
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))
278
215
 
279
 
        self.maybe_die()  # should be able to survive couchdb death
280
216
        self.assertTrue(len(record_ids_we_care_about) == 0, "expected zero")
281
217
 
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)
292
228
 
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)
296
231
 
298
233
 
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.
304
238
 
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)
309
242
 
310
243
    def test_get_changes(self):
364
297
        # Ensure time is same.
365
298
        self.assertEqual(saved_time, self.database._changes_last_used)
366
299
 
367
 
        ###self.maybe_die()  # should be able to survive couchdb death
368
 
 
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)
376
307
 
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"
380
 
 
381
 
        map_js = """function(doc) { emit(doc._id, null) }"""
382
 
 
383
 
        self.database.add_view(view1_name, map_js, None, design_doc)
384
 
        view = self.database.execute_view(view1_name, design_doc)
385
 
        
386
 
        for attr_name in dir(view.obj):
387
 
            # This works because hasattr actually calls getattr.
388
 
            self.assertTrue(hasattr(view, attr_name))
389
 
 
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")
430
348
 
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.
434
 
 
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")
443
357
 
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)
447
360
 
452
365
        # get new
453
366
        retrieved_record = self.database.get_record(record_id)
454
367
 
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,
458
 
                "another document")
459
 
 
460
 
        # push new version
461
 
        record_id = self.database.put_record(retrieved_record)
462
 
        # get new
463
 
        retrieved_record = self.database.get_record(record_id)
464
 
 
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"]))
469
 
 
470
 
        # Send back name.
471
370
        retrieved_record.attach(content, "another document", "text/plain")
472
371
 
473
372
        # push new version
485
384
        self.assertEqual(out_data, content.getvalue())
486
385
        self.assertEqual(out_content_type, "text/plain")
487
386
 
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,
491
389
                "NoExist")
516
414
        # ordinary requests are in key order
517
415
        self.assertEqual(data, sorted(data))
518
416
 
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,
576
473
            self.fail()
577
474
        except FieldsConflict, e:
578
475
            self.assertEqual({('field1',): (22, 11)}, e.conflicts)
579
 
 
580
 
 
581
 
class TestServerDiesSegv(TestCouchDatabase):
582
 
    def get_test_context(self):
583
 
        try:
584
 
            return self.ctx
585
 
        except AttributeError:
586
 
            self.ctx = test_environment.create_new_test_environment()
587
 
            return self.ctx
588
 
 
589
 
    def maybe_die(self):
590
 
        self.database.ensure_full_commit()
591
 
        time.sleep(2)
592
 
        pid = find_pid(start_if_not_running=False, ctx=self.get_test_context())
593
 
        if pid is None:
594
 
            print "couchdb has already quit!  That's unexpected."
595
 
            return
596
 
        print "DIE, process", pid, "!"
597
 
        os.kill(pid, signal.SIGSEGV)
598
 
        self.wait_until_server_dead(pid=pid)
599
 
 
600
 
 
601
 
class TestServerDiesNormal(TestCouchDatabase):
602
 
    def get_test_context(self):
603
 
        try:
604
 
            return self.ctx
605
 
        except AttributeError:
606
 
            self.ctx = test_environment.create_new_test_environment()
607
 
            return self.ctx
608
 
 
609
 
    def maybe_die(self):
610
 
        self.database.ensure_full_commit()
611
 
        time.sleep(2)
612
 
        stop_couchdb(ctx=self.get_test_context())
613
 
        self.wait_until_server_dead()