couch-client 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe CouchClient do
4
+ it 'should exist' do
5
+ CouchClient.should be_a(Module)
6
+ end
7
+
8
+ it 'should have a .connect method that constructs a new CouchClient::Connection object' do
9
+ CouchClient.connect(:database => "sandbox").should be_a(CouchClient::Connection)
10
+ end
11
+ end
@@ -0,0 +1,44 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe CouchClient::Database do
4
+ before(:all) do
5
+ @couch = CouchClient.connect(COUCHDB_TEST_SETTINGS)
6
+ @couch.database.create
7
+ end
8
+
9
+ after(:all) do
10
+ @couch.database.delete!
11
+ end
12
+
13
+ describe '#stats' do
14
+ it 'should exist' do
15
+ @couch.database.stats.should be_a(Hash)
16
+ end
17
+ end
18
+
19
+ describe '#exists?' do
20
+ it 'should exist' do
21
+ @couch.database.exists?.should be_a(TrueClass)
22
+ end
23
+ end
24
+
25
+ describe '#create' do
26
+ # Is already tested as it is used in the before(:all) setup
27
+ # to make the database that is currently being tested.
28
+ end
29
+
30
+ describe '#delete' do
31
+ # Is already tested as it is used in the after(:all) teardown
32
+ # to delete the database that is currently being tested.
33
+ end
34
+
35
+ describe '#compact!' do
36
+ it 'should exist' do
37
+ @couch.database.compact!.should be_a(Hash)
38
+ end
39
+ end
40
+
41
+ describe '#replicate' do
42
+ pending 'will be built in another release'
43
+ end
44
+ end
@@ -0,0 +1,100 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe CouchClient::Connection do
4
+ before(:all) do
5
+ @couch = CouchClient.connect(COUCHDB_TEST_SETTINGS)
6
+ @couch.database.create
7
+
8
+ factory = lambda do |hash|
9
+ doc = @couch.build(hash)
10
+ doc.save
11
+ doc
12
+ end
13
+
14
+ @alice = factory.call({"_id" => "123", "name" => "alice", "city" => "nyc"})
15
+ @bob = factory.call({"_id" => "456", "name" => "bob", "city" => "chicago"})
16
+ @charlie = factory.call({"_id" => "789", "name" => "charlie", "city" => "san fran"})
17
+ @design = factory.call({"_id" => "_design/people",
18
+ "views" => {
19
+ "all" => {"map" => "function(doc){emit(doc._id, doc)}"},
20
+ "sum" => {"map" => "function(doc){emit(null, 1)}", "reduce" => "function(id, values, rereduce){return sum(values)}"},
21
+ },
22
+ "fulltext" => {
23
+ "by_name" => {
24
+ "index" => "function(doc){var ret = new Document();ret.add(doc.name);return ret;}"
25
+ }
26
+ }
27
+ })
28
+
29
+ @people = @couch.design("people")
30
+ end
31
+
32
+ after(:all) do
33
+ @couch.database.delete!
34
+ end
35
+
36
+ it 'should have an id' do
37
+ @people.id.should eql("people")
38
+ end
39
+
40
+ describe '#view' do
41
+ it 'should return a mapped collection if the view exists' do
42
+ view = @people.view("all", "include_docs" => true)
43
+ view.should be_a(CouchClient::Collection)
44
+ view.info.should be_a(Hash)
45
+ view.size.should eql(3)
46
+ view.first.keys.should eql(["id", "key", "value", "doc"])
47
+ view.first["id"].should eql(@alice.id)
48
+ view.last["id"].should eql(@charlie.id)
49
+ end
50
+
51
+ it 'should return mapped results based on "key"' do
52
+ view = @people.view("all", "key" => @bob.id)
53
+ view.size.should eql(1)
54
+ view.first["id"].should eql(@bob.id)
55
+ end
56
+
57
+ it 'should return mapped results based on "startkey"' do
58
+ view = @people.view("all", "startkey" => @bob.id)
59
+ view.size.should eql(2)
60
+ view.first["id"].should eql(@bob.id)
61
+ view.last["id"].should eql(@charlie.id)
62
+ end
63
+
64
+ it 'should return mapped results based on "endkey"' do
65
+ view = @people.view("all", "endkey" => @bob.id)
66
+ view.size.should eql(2)
67
+ view.first["id"].should eql(@alice.id)
68
+ end
69
+
70
+ it 'should return a mapped and reduced collection if the view exists' do
71
+ view = @people.view("sum", "group" => true)
72
+ view.should be_a(CouchClient::Collection)
73
+ view.info.should be_a(Hash)
74
+ view.size.should eql(1)
75
+ view.first.keys.should eql(["key", "value"])
76
+ view.first["value"].should eql(3)
77
+ end
78
+ end
79
+
80
+ describe '#show' do
81
+ pending 'will be built in another release'
82
+ end
83
+
84
+ describe '#list' do
85
+ pending 'will be built in another release'
86
+ end
87
+
88
+ describe '#fulltext' do
89
+ it 'should return a lucine status hash if the fulltext exists' do
90
+ @people.fulltext("by_name").should be_a(Hash)
91
+ end
92
+
93
+ it 'should return a search results collection if the fulltext exists and a query is given' do
94
+ fulltext = @people.fulltext("by_name", "q" => "al*")
95
+ fulltext.should be_a(CouchClient::Collection)
96
+ fulltext.info.should be_a(Hash)
97
+ fulltext.first["id"].should eql(@alice.id)
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,196 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+ require 'digest/sha1'
3
+
4
+ describe CouchClient::Document do
5
+ before(:each) do
6
+ @couch = CouchClient.connect(COUCHDB_TEST_SETTINGS)
7
+ @couch.database.create
8
+
9
+ factory = lambda do |hash|
10
+ doc = @couch.build(hash)
11
+ doc.save
12
+ doc
13
+ end
14
+
15
+ @alice = factory.call({"name" => "alice", "city" => "nyc"})
16
+ @bob = factory.call({"name" => "bob", "city" => "chicago"})
17
+ @new = @couch.build
18
+ @design = factory.call({"_id" => "_design/people", "views" => {"all" => {"map" => "function(doc){emit(doc._id, doc)}"}}})
19
+ end
20
+
21
+ after(:each) do
22
+ @couch.database.delete!
23
+ end
24
+
25
+ it 'should have #id, #rev and #attachments methods' do
26
+ @alice.id.should eql(@alice["_id"])
27
+ @alice.rev.should eql(@alice["_rev"])
28
+ @alice.attachments.should eql(@alice["_attachments"])
29
+ end
30
+
31
+ it 'should have #id=, #rev= and #attachments= methods' do
32
+ @alice.should respond_to(:id=)
33
+ @alice.should respond_to(:rev=)
34
+ @alice.should respond_to(:attachments=)
35
+ end
36
+
37
+ describe '#saved_doc' do
38
+ it 'should get the doc from the database' do
39
+ @alice["key"] = "value"
40
+ @alice.saved_doc.should_not eql(@alice)
41
+
42
+ @alice["key"].should eql("value")
43
+ @alice.saved_doc["key"].should be_nil
44
+ end
45
+
46
+ it 'should return nothing if the doc is new' do
47
+ lambda{@new.saved_doc}.should raise_error(CouchClient::DocumentNotAvailable)
48
+ end
49
+ end
50
+
51
+ describe '#save' do
52
+ it 'should save a new document' do
53
+ @new.new?.should be_true
54
+ @new.rev.should be_nil
55
+
56
+ @new.save
57
+
58
+ @new.new?.should be_false
59
+ @new.rev.should_not be_nil
60
+ end
61
+
62
+ it 'should update an existing document' do
63
+ @alice["key"].should be_nil
64
+ @alice["key"] = "value"
65
+
66
+ @alice.save
67
+
68
+ @alice["key"].should eql("value")
69
+ @alice.saved_doc["key"].should eql("value")
70
+ end
71
+
72
+ it 'should not save a document in conflict' do
73
+ @alice_old = @alice.saved_doc
74
+ @alice_old["old"] = true
75
+ @alice["key"] = "value"
76
+
77
+ @alice.save
78
+
79
+ @alice_old.save
80
+ @alice_old.error?.should be_true
81
+ @alice_old.conflict?.should be_true
82
+ @alice_old.error.should eql({"conflict"=>"Document update conflict."})
83
+ end
84
+ end
85
+
86
+ describe '#attach' do
87
+ before(:all) do
88
+ @read = lambda do |file|
89
+ File.read(File.join(File.dirname(__FILE__), "files", file))
90
+ end
91
+
92
+ @digest = lambda do |file|
93
+ Digest::SHA1.hexdigest(file)
94
+ end
95
+
96
+ @plain = @read.call("plain.txt")
97
+ @image = @read.call("image.png")
98
+
99
+ @plain_digest = @digest.call(@plain)
100
+ @image_digest = @digest.call(@image)
101
+ end
102
+
103
+ it 'should attach a file' do
104
+ @alice.attach("plain.txt", @plain, "text/plain")
105
+ @alice.attach("image.png", @image, "image/png")
106
+ @alice = @alice.saved_doc
107
+
108
+ @alice.attachments.should eql({"image.png"=>{"content_type"=>"image/png", "revpos"=>3, "length"=>104744, "stub"=>true}, "plain.txt"=>{"content_type"=>"text/plain", "revpos"=>2, "length"=>406, "stub"=>true}})
109
+ @digest.call(@alice.attachments["plain.txt"].data).should eql(@plain_digest)
110
+ @digest.call(@alice.attachments["image.png"].data).should eql(@image_digest)
111
+ end
112
+
113
+ it 'should not attach a file to a new record' do
114
+ lambda{@new.attach("plain.txt", @plain, "text/plain")}.should raise_error(CouchClient::AttachmentError)
115
+ end
116
+ end
117
+
118
+ describe '#delete!' do
119
+ it 'should delete a docment' do
120
+ @bob.deleted?.should be_false
121
+ @bob.delete!
122
+ @bob.deleted?.should be_true
123
+ lambda{@bob.saved_doc}.should raise_error(CouchClient::DocumentNotFound)
124
+ end
125
+
126
+ it 'should not delete a document in conflict' do
127
+ @alice_old = @alice.saved_doc
128
+ @alice_old["old"] = true
129
+ @alice["key"] = "value"
130
+
131
+ @alice.save
132
+
133
+ @alice_old.delete!
134
+ @alice_old.error?.should be_true
135
+ @alice_old.conflict?.should be_true
136
+ @alice_old.error.should eql({"conflict"=>"Document update conflict."})
137
+ end
138
+ end
139
+
140
+ describe '#design?' do
141
+ it 'should identify a design document' do
142
+ @design.design?.should be_true
143
+ end
144
+
145
+ it 'should not identify a normal document' do
146
+ @alice.design?.should be_false
147
+ end
148
+ end
149
+
150
+ describe '#new?' do
151
+ it 'should identify a new document' do
152
+ @alice.new?.should be_false
153
+ end
154
+
155
+ it 'should not identify an existing document' do
156
+ @new.new?.should be_true
157
+ end
158
+ end
159
+
160
+ describe '#error #error? and conflict?' do
161
+ it 'should yield errors for a document that has errors' do
162
+ @alice_old = @alice.saved_doc
163
+ @alice["key"] = "value"
164
+
165
+ @alice_old.error?.should be_false
166
+ @alice_old.error.should eql({})
167
+ @alice_old.conflict?.should be_false
168
+
169
+ @alice.save
170
+ @alice_old.save
171
+
172
+ @alice_old.error?.should be_true
173
+ @alice_old.error.should eql({"conflict"=>"Document update conflict."})
174
+ @alice_old.conflict?.should be_true
175
+ end
176
+ end
177
+
178
+ describe '#invalid?' do
179
+ before(:each) do
180
+ @alice.instance_variable_set(:@code, 403)
181
+ @alice.instance_variable_set(:@error, {"forbidden" => "Document must have a name field."})
182
+ end
183
+
184
+ it 'should identify an invalid document' do
185
+ @alice.invalid?.should be_true
186
+ end
187
+ end
188
+
189
+ describe '#deleted?' do
190
+ it 'should identify a deleted document' do
191
+ @bob.deleted?.should be_false
192
+ @bob.delete!
193
+ @bob.deleted?.should be_true
194
+ end
195
+ end
196
+ end
Binary file
@@ -0,0 +1 @@
1
+ When in the Course of human events, it becomes necessary for one people to dissolve the political bands which have connected them with another, and to assume among the powers of the earth, the separate and equal station to which the Laws of Nature and of Nature's God entitle them, a decent respect to the opinions of mankind requires that they should declare the causes which impel them to the separation.
@@ -0,0 +1,122 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+ require 'digest/sha1'
3
+
4
+ describe CouchClient::Hookup do
5
+ before(:all) do
6
+ handler = CouchClient::ConnectionHandler.new
7
+ handler.database = COUCHDB_TEST_DATABASE
8
+
9
+ @hookup = CouchClient::Hookup.new(handler)
10
+ end
11
+
12
+ after(:all) do
13
+ @hookup.delete
14
+ end
15
+
16
+ describe 'rest methods' do
17
+ describe 'put' do
18
+ it 'should create a database if one does not exist' do
19
+ @hookup.put.should eql([201, {"ok" => true}])
20
+ end
21
+
22
+ it 'should not create a database if one already exists' do
23
+ @hookup.put.should eql([412, {"error" => "file_exists", "reason" => "The database could not be created, the file already exists."}])
24
+ end
25
+
26
+ it 'should create a document if a path is provided' do
27
+ @hookup.put(["alice"]).should eql([201, {"ok" => true, "id" => "alice", "rev" => "1-967a00dff5e02add41819138abb3284d"}])
28
+ end
29
+
30
+ it 'should create a document with fields if a path with data is provided' do
31
+ @hookup.put(["bob"], nil, {"name" => "bob", "city" => "nyc"}).should eql([201, {"ok" => true, "id" => "bob", "rev" => "1-fb4ceea745e7c1cd487886f06eba6536"}])
32
+ end
33
+ end
34
+
35
+ describe 'post' do
36
+ before(:all) do
37
+ @hookup.put
38
+ end
39
+
40
+ it 'should create a document' do
41
+ code, body = @hookup.post(nil, nil, {"name" => "charlie"})
42
+ code.should eql(201)
43
+ body.should be_a(Hash)
44
+ end
45
+ end
46
+
47
+ describe 'get and head' do
48
+ before(:all) do
49
+ @hookup.put
50
+ @hookup.put(["dave"], nil, {"name" => "dave", "city" => "chicago"})
51
+ @id = @hookup.post(nil, nil, {"name" => "edgar", "city" => "miami"}).last["id"]
52
+ end
53
+
54
+ it 'should get database information when not given a path' do
55
+ code, body = @hookup.get
56
+ code.should eql(200)
57
+ body["db_name"].should eql("couch-client_test")
58
+ end
59
+
60
+ it 'should get a document when given a path' do
61
+ @hookup.get(["dave"]).should eql([200, {"_id" => "dave", "_rev" => "1-17a22c4b658fd637577a4626344be252", "name" => "dave", "city" => "chicago"}])
62
+ code, body = @hookup.get([@id])
63
+ code.should eql(200)
64
+ body["name"].should eql("edgar")
65
+ body["city"].should eql("miami")
66
+ end
67
+
68
+ it 'should head a document when given a path' do
69
+ @hookup.head(["dave"]).should eql([200, nil])
70
+ code, body = @hookup.head([@id])
71
+ code.should eql(200)
72
+ body.should be_nil
73
+ end
74
+ end
75
+
76
+ describe 'delete' do
77
+ before(:all) do
78
+ @hookup.put
79
+ @rev = @hookup.put(["fred"], nil, {"name" => "fred", "city" => "san fran"}).last["rev"]
80
+ end
81
+
82
+ it 'should delete a document when given a path' do
83
+ @hookup.delete(["fred"], {"rev" => @rev}).should eql([200, {"ok" => true, "id" => "fred", "rev" => "2-9bee1aef2fee82160ae8549079645933"}])
84
+ end
85
+
86
+ it 'should delete the database when not given a path' do
87
+ @hookup.delete.should eql([200, {"ok" => true}])
88
+ end
89
+ end
90
+ end
91
+
92
+ describe 'attachments' do
93
+ before(:all) do
94
+ @read = lambda do |file|
95
+ File.read(File.join(File.dirname(__FILE__), "files", file))
96
+ end
97
+
98
+ @digest = lambda do |file|
99
+ Digest::SHA1.hexdigest(file)
100
+ end
101
+
102
+ @plain = @read.call("plain.txt")
103
+ @image = @read.call("image.png")
104
+
105
+ @plain_digest = @digest.call(@plain)
106
+ @image_digest = @digest.call(@image)
107
+
108
+ @hookup.put
109
+ @rev = @hookup.put(["greg"], nil, {"name" => "greg", "city" => "austin"}).last["rev"]
110
+ end
111
+
112
+ it 'can be uploaded' do
113
+ @rev = @hookup.put(["greg", "plain.txt"], {"rev" => @rev}, @plain, "text/plain").last["rev"]
114
+ @rev = @hookup.put(["greg", "image.png"], {"rev" => @rev}, @image, "image/png").last["rev"]
115
+ end
116
+
117
+ it 'can be downloaded' do
118
+ @digest.call(@hookup.get(["greg", "plain.txt"], nil, "text/plain").last).should eql(@plain_digest)
119
+ @digest.call(@hookup.get(["greg", "image.png"], nil, "image/png").last).should eql(@image_digest)
120
+ end
121
+ end
122
+ end