analysand 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,478 @@
1
+ require 'spec_helper'
2
+
3
+ require 'analysand/database'
4
+ require 'analysand/errors'
5
+
6
+ module Analysand
7
+ describe Database do
8
+ let(:db) { Database.new(database_uri) }
9
+
10
+ let(:doc_id) { 'abc123' }
11
+ let(:doc) do
12
+ { 'foo' => 'bar' }
13
+ end
14
+
15
+ before do
16
+ clean_databases!
17
+ clear_security
18
+ end
19
+
20
+ shared_examples_for '#put success examples' do
21
+ def put(*args)
22
+ db.send(method, *args)
23
+ end
24
+
25
+ it 'creates documents' do
26
+ put(doc_id, doc)
27
+
28
+ db.get(doc_id).body['foo'].should == 'bar'
29
+ end
30
+
31
+ it 'returns success on document creation' do
32
+ resp = put(doc_id, doc)
33
+
34
+ resp.should be_success
35
+ end
36
+
37
+ it 'ignores the _id attribute in documents' do
38
+ doc.update('_id' => 'wrong')
39
+ put(doc_id, doc)
40
+
41
+ db.get(doc_id).body['foo'].should == 'bar'
42
+ db.get('wrong').should_not be_success
43
+ end
44
+
45
+ it 'passes credentials' do
46
+ set_security({ 'users' => [member1_username] })
47
+
48
+ put(doc_id, doc, member1_credentials)
49
+ db.get(doc_id).should be_success
50
+ end
51
+
52
+ it 'passes the batch option' do
53
+ resp = put(doc_id, doc, nil, :batch => 'ok')
54
+
55
+ resp.code.should == '202'
56
+ end
57
+
58
+ it 'updates documents' do
59
+ resp = put(doc_id, doc)
60
+
61
+ doc['bar'] = 'baz'
62
+ doc['_rev'] = resp['rev']
63
+
64
+ put(doc_id, doc)
65
+ db.get(doc_id)['bar'].should == 'baz'
66
+ end
67
+
68
+ it 'returns success on document update' do
69
+ resp = put(doc_id, doc)
70
+
71
+ doc['bar'] = 'baz'
72
+ doc['_rev'] = resp['rev']
73
+
74
+ resp = put(doc_id, doc)
75
+ resp.should be_success
76
+ end
77
+
78
+ it 'escapes document IDs' do
79
+ db.put('an ID', doc)
80
+
81
+ db.get('an ID').should be_success
82
+ end
83
+
84
+ it 'handles URN-like IDs' do
85
+ db.put('org.couchdb.doc:one', doc)
86
+
87
+ db.get('org.couchdb.doc:one').should be_success
88
+ end
89
+ end
90
+
91
+ describe '#create' do
92
+ before do
93
+ drop_databases!
94
+ end
95
+
96
+ it 'creates a database' do
97
+ db.create(admin_credentials)
98
+
99
+ db.ping.should be_success
100
+ end
101
+
102
+ it 'returns success' do
103
+ db.create(admin_credentials).should be_success
104
+ end
105
+ end
106
+
107
+ describe '#drop' do
108
+ it 'drops the database' do
109
+ db.drop(admin_credentials)
110
+
111
+ db.ping.should_not be_success
112
+ end
113
+
114
+ it 'returns success' do
115
+ db.drop(admin_credentials).should be_success
116
+ end
117
+ end
118
+
119
+ describe '#drop!' do
120
+ it 'drops the database' do
121
+ db.drop(admin_credentials)
122
+
123
+ db.ping.should_not be_success
124
+ end
125
+
126
+ it 'raises Analysand::CannotDropDatabase on failure' do
127
+ lambda { db.drop!(member1_credentials) }.should raise_error(Analysand::CannotDropDatabase)
128
+ end
129
+ end
130
+
131
+ describe '#put' do
132
+ it_should_behave_like '#put success examples' do
133
+ let(:method) { :put }
134
+ end
135
+
136
+ describe 'on update conflict' do
137
+ before do
138
+ db.put(doc_id, doc)
139
+ end
140
+
141
+ it 'returns the error code' do
142
+ resp = db.put(doc_id, doc)
143
+
144
+ resp.code.should == '409'
145
+ end
146
+
147
+ it 'returns the error body' do
148
+ resp = db.put(doc_id, doc)
149
+
150
+ resp.body.should have_key('error')
151
+ end
152
+ end
153
+ end
154
+
155
+ describe '#put!' do
156
+ it_should_behave_like '#put success examples' do
157
+ let(:method) { :put! }
158
+ end
159
+
160
+ describe 'on update conflict' do
161
+ before do
162
+ db.put(doc_id, doc)
163
+ end
164
+
165
+ it 'raises Analysand::DocumentNotSaved' do
166
+ lambda { db.put!(doc_id, doc) }.should raise_error(Analysand::DocumentNotSaved) { |e|
167
+ e.response.code.should == '409'
168
+ }
169
+ end
170
+ end
171
+ end
172
+
173
+ describe '#ensure_full_commit' do
174
+ before do
175
+ db.put('abc', {}, :batch => :ok)
176
+ end
177
+
178
+ it 'returns success' do
179
+ db.ensure_full_commit.should be_success
180
+ end
181
+
182
+ it 'flushes batched PUTs' do
183
+ db.ensure_full_commit
184
+
185
+ db.get('abc').should be_success
186
+ end
187
+
188
+ it 'accepts credentials' do
189
+ lambda { db.ensure_full_commit(member1_credentials) }.should_not raise_error(ArgumentError)
190
+ end
191
+
192
+ it 'accepts a seq parameter' do
193
+ lambda { db.ensure_full_commit(member1_credentials, :seq => 10) }.should_not raise_error(ArgumentError)
194
+ end
195
+ end
196
+
197
+ describe '#put_attachment' do
198
+ let(:string) { 'an attachment' }
199
+ let(:io) { StringIO.new(string) }
200
+
201
+ it 'creates attachments' do
202
+ db.put_attachment("#{doc_id}/attachment", io)
203
+
204
+ db.get_attachment("#{doc_id}/attachment").body.should == string
205
+ end
206
+
207
+ it 'returns success when the attachment is uploaded' do
208
+ resp = db.put_attachment("#{doc_id}/attachment", io)
209
+
210
+ resp.should be_success
211
+ end
212
+
213
+ it 'passes credentials' do
214
+ set_security({ 'users' => [member1_username] })
215
+
216
+ resp = db.put_attachment("#{doc_id}/a", io, member1_credentials)
217
+ resp.should be_success
218
+ end
219
+
220
+ it 'sends the rev of the target document' do
221
+ resp = db.put!(doc_id, doc)
222
+ rev = resp['rev']
223
+
224
+ resp = db.put_attachment("#{doc_id}/a", io, nil, :rev => rev)
225
+ resp.should be_success
226
+ end
227
+
228
+ it 'sends the content type of the attachment' do
229
+ db.put_attachment("#{doc_id}/a", io, nil, :content_type => 'text/plain')
230
+
231
+ type = db.get_attachment("#{doc_id}/a").get_fields('Content-Type')
232
+ type.should == ['text/plain']
233
+ end
234
+ end
235
+
236
+ describe '#bulk_docs' do
237
+ let(:doc1) { { '_id' => 'doc1', 'foo' => 'bar' } }
238
+ let(:doc2) { { '_id' => 'doc2', 'bar' => 'baz' } }
239
+
240
+ it 'creates many documents' do
241
+ db.bulk_docs([doc1, doc2])
242
+
243
+ db.get('doc1')['foo'].should == 'bar'
244
+ db.get('doc2')['bar'].should == 'baz'
245
+ end
246
+
247
+ it 'updates many documents' do
248
+ r1 = db.put!('doc1', doc1)
249
+ r2 = db.put!('doc2', doc2)
250
+
251
+ doc1['foo'] = 'qux'
252
+ doc2['bar'] = 'quux'
253
+ doc1['_rev'] = r1['rev']
254
+ doc2['_rev'] = r2['rev']
255
+
256
+ db.bulk_docs([doc1, doc2])
257
+
258
+ db.get('doc1')['foo'].should == 'qux'
259
+ db.get('doc2')['bar'].should == 'quux'
260
+ end
261
+
262
+ it 'deletes many documents' do
263
+ r1 = db.put!('doc1', doc1)
264
+ r2 = db.put!('doc2', doc2)
265
+
266
+ d1 = { '_id' => 'doc1', '_rev' => r1['rev'], '_deleted' => true }
267
+ d2 = { '_id' => 'doc2', '_rev' => r2['rev'], '_deleted' => true }
268
+
269
+ db.bulk_docs([d1, d2])
270
+
271
+ db.get('doc1').code.should == '404'
272
+ db.get('doc2').code.should == '404'
273
+ end
274
+
275
+ it 'updates and deletes documents' do
276
+ r1 = db.put!('doc1', doc1)
277
+ r2 = db.put!('doc2', doc2)
278
+
279
+ d1 = { '_id' => 'doc1', '_rev' => r1['rev'], '_deleted' => true }
280
+ d2 = { '_id' => 'doc2', '_rev' => r2['rev'], 'bar' => 'quux' }
281
+
282
+ db.bulk_docs([d1, d2])
283
+
284
+ db.get('doc1').code.should == '404'
285
+ db.get('doc2')['bar'].should == 'quux'
286
+ end
287
+
288
+ it 'returns success if all operations succeeded' do
289
+ resp = db.bulk_docs([doc1, doc2])
290
+
291
+ resp.should be_success
292
+ end
293
+
294
+ it 'returns non-success if one operation had an error' do
295
+ db.put!('doc1', doc1)
296
+
297
+ resp = db.bulk_docs([doc1, doc2])
298
+
299
+ resp.should_not be_success
300
+ end
301
+
302
+ it 'passes credentials' do
303
+ set_security({ 'users' => [member1_username] })
304
+
305
+ resp = db.bulk_docs([doc1, doc2], member1_credentials)
306
+
307
+ resp.should be_success
308
+ end
309
+
310
+ it 'operates in non-atomic mode by default' do
311
+ db.put!('doc1', doc1)
312
+ db.bulk_docs([doc1, doc2])
313
+
314
+ db.get('doc2').should be_success
315
+ end
316
+
317
+ it 'supports all-or-nothing mode' do
318
+ # Force a validation failure to check that all-or-nothing mode is
319
+ # properly enabled.
320
+ db.put!('doc1', doc1)
321
+ db.put!('_design/validation', {
322
+ 'validate_doc_update' => 'function(){throw({forbidden: ""});}'
323
+ }, admin_credentials)
324
+
325
+ db.bulk_docs([doc1, doc2], nil, :all_or_nothing => true)
326
+
327
+ db.get('doc2').code.should == '404'
328
+ end
329
+ end
330
+
331
+ describe '#bulk_docs!' do
332
+ let(:doc1) { { '_id' => 'doc1', 'foo' => 'bar' } }
333
+ let(:doc2) { { '_id' => 'doc2', 'bar' => 'baz' } }
334
+
335
+ it 'returns success if all operations succeeded' do
336
+ resp = db.bulk_docs!([doc1, doc2])
337
+
338
+ resp.should be_success
339
+ end
340
+
341
+ describe 'if an operation fails' do
342
+ before do
343
+ doc2['_id'] = 'doc1'
344
+ end
345
+
346
+ it 'raises Analysand::BulkOperationFailed' do
347
+ lambda { db.bulk_docs!([doc1, doc2]) }.should raise_error(Analysand::BulkOperationFailed)
348
+ end
349
+ end
350
+ end
351
+
352
+ describe '#copy' do
353
+ before do
354
+ db.put!(doc_id, doc)
355
+ end
356
+
357
+ it 'copies one doc to another ID' do
358
+ db.copy(doc_id, 'bar')
359
+
360
+ db.get('bar')['foo'].should == 'bar'
361
+ end
362
+
363
+ it 'returns success if copy succeeds' do
364
+ resp = db.copy(doc_id, 'bar')
365
+
366
+ resp.should be_success
367
+ end
368
+
369
+ it 'returns failure if copy fails' do
370
+ db.put!('bar', {})
371
+ resp = db.copy(doc_id, 'bar')
372
+
373
+ resp.code.should == '409'
374
+ end
375
+
376
+ it 'overwrites documents' do
377
+ resp = db.put!('bar', {})
378
+ db.copy(doc_id, "bar?rev=#{resp['rev']}")
379
+
380
+ db.get('bar')['foo'].should == 'bar'
381
+ end
382
+
383
+ it 'passes credentials' do
384
+ set_security({ 'users' => [member1_username] })
385
+
386
+ db.copy(doc_id, 'bar', member1_credentials)
387
+ db.get('bar')['foo'].should == 'bar'
388
+ end
389
+
390
+ it 'escapes document IDs in URIs' do
391
+ db.copy(doc_id, 'an ID')
392
+
393
+ db.get('an ID')['foo'].should == 'bar'
394
+ end
395
+ end
396
+
397
+ shared_examples_for '#delete success examples' do
398
+ let(:rev) { @put_resp['rev'] }
399
+
400
+ before do
401
+ @put_resp = db.put!(doc_id, doc)
402
+ end
403
+
404
+ def delete(*args)
405
+ db.send(method, *args)
406
+ end
407
+
408
+ it 'deletes documents' do
409
+ db.delete(doc_id, rev)
410
+
411
+ db.get(doc_id).code.should == '404'
412
+ end
413
+
414
+ it 'returns success on deletion' do
415
+ resp = db.delete(doc_id, rev)
416
+
417
+ resp.should be_success
418
+ end
419
+
420
+ it 'passes credentials' do
421
+ set_security({ 'users' => [member1_username] })
422
+
423
+ resp = db.delete(doc_id, rev, member1_credentials)
424
+
425
+ resp.should be_success
426
+ end
427
+
428
+ it 'escapes document IDs in URIs' do
429
+ @put_resp = db.put!('an ID', doc)
430
+
431
+ resp = db.delete('an ID', rev)
432
+ resp.should be_success
433
+ end
434
+ end
435
+
436
+ describe '#delete' do
437
+ it_should_behave_like '#delete success examples' do
438
+ let(:method) { :delete }
439
+ end
440
+
441
+ describe 'on update conflict' do
442
+ before do
443
+ db.put!(doc_id, doc)
444
+ end
445
+
446
+ it 'returns the error code' do
447
+ resp = db.delete(doc_id, nil)
448
+
449
+ resp.code.should == '400'
450
+ end
451
+
452
+ it 'returns the error body' do
453
+ resp = db.delete(doc_id, nil)
454
+
455
+ resp.body.should have_key('error')
456
+ end
457
+ end
458
+ end
459
+
460
+ describe '#delete!' do
461
+ it_should_behave_like '#delete success examples' do
462
+ let(:method) { :delete! }
463
+ end
464
+
465
+ describe 'on update conflict' do
466
+ before do
467
+ db.put!(doc_id, doc)
468
+ end
469
+
470
+ it 'raises Analysand::DocumentNotDeleted' do
471
+ lambda { db.delete!(doc_id, nil) }.should raise_error(Analysand::DocumentNotDeleted) { |e|
472
+ e.response.code.should == '400'
473
+ }
474
+ end
475
+ end
476
+ end
477
+ end
478
+ end