simply_stored 0.3.6 → 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,240 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../fixtures/couch')
3
+
4
+ class CouchTest < Test::Unit::TestCase
5
+ context "A simply stored couch instance" do
6
+ setup do
7
+ CouchPotato::Config.database_name = 'simply_stored_test'
8
+ recreate_db
9
+ end
10
+
11
+ context "design documents" do
12
+ should "delete all" do
13
+ db = "http://127.0.0.1:5984/#{CouchPotato::Config.database_name}"
14
+ assert_equal 0, SimplyStored::Couch.delete_all_design_documents(db)
15
+ user = User.create
16
+ Post.create(:user => user)
17
+ user.posts
18
+ assert_equal 1, SimplyStored::Couch.delete_all_design_documents(db)
19
+ end
20
+ end
21
+
22
+ context "when creating instances" do
23
+ should "populate the attributes" do
24
+ user = User.create(:title => "Mr.", :name => "Host Master")
25
+ assert_equal "Mr.", user.title
26
+ assert_equal "Host Master", user.name
27
+ end
28
+
29
+ should "save the instance" do
30
+ user = User.create(:title => "Mr.")
31
+ assert !user.new_record?
32
+ end
33
+
34
+ context "with a bang" do
35
+ should 'not raise an exception when saving succeeded' do
36
+ assert_nothing_raised do
37
+ User.create!(:title => "Mr.")
38
+ end
39
+ end
40
+
41
+ should 'save the user' do
42
+ user = User.create!(:title => "Mr.")
43
+ assert !user.new_record?
44
+ end
45
+
46
+ should 'raise an error when the validations failed' do
47
+ assert_raises(CouchPotato::Database::ValidationsFailedError) do
48
+ User.create!(:title => nil)
49
+ end
50
+ end
51
+ end
52
+
53
+ context "with a block" do
54
+ should 'call the block with the record' do
55
+ user = User.create do |u|
56
+ u.title = "Mr."
57
+ end
58
+
59
+ assert_equal "Mr.", user.title
60
+ end
61
+
62
+ should 'save the record' do
63
+ user = User.create do |u|
64
+ u.title = "Mr."
65
+ end
66
+ assert !user.new_record?
67
+ end
68
+
69
+ should 'assign attributes via the hash' do
70
+ user = User.create(:title => "Mr.") do |u|
71
+ u.name = "Host Master"
72
+ end
73
+
74
+ assert_equal "Mr.", user.title
75
+ assert_equal "Host Master", user.name
76
+ end
77
+ end
78
+ end
79
+
80
+ context "when saving an instance" do
81
+ should "um, save the instance" do
82
+ user = User.new(:title => "Mr.")
83
+ assert user.new_record?
84
+ user.save
85
+ assert !user.new_record?
86
+ end
87
+
88
+ context "when using save!" do
89
+ should 'raise an exception when a validation isnt fulfilled' do
90
+ user = User.new
91
+ assert_raises(CouchPotato::Database::ValidationsFailedError) do
92
+ user.save!
93
+ end
94
+ end
95
+ end
96
+
97
+ context "when using save(false)" do
98
+ should "not run the validations" do
99
+ user = User.new
100
+ user.save(false)
101
+ assert !user.new?
102
+ assert !user.dirty?
103
+ end
104
+ end
105
+ end
106
+
107
+ context "when destroying an instance" do
108
+ should "remove the instance" do
109
+ user = User.create(:title => "Mr")
110
+ assert_difference 'User.find(:all).size', -1 do
111
+ user.destroy
112
+ end
113
+ end
114
+
115
+ should 'return the frozen instance, brrrr' do
116
+ user = User.create(:title => "Mr")
117
+ assert_equal user, user.destroy
118
+ end
119
+ end
120
+
121
+ context "when updating attributes" do
122
+ should "merge in the updated attributes" do
123
+ user = User.create(:title => "Mr.")
124
+ user.update_attributes(:title => "Mrs.")
125
+ assert_equal "Mrs.", user.title
126
+ end
127
+
128
+ should "save the instance" do
129
+ user = User.create(:title => "Mr.")
130
+ user.update_attributes(:title => "Mrs.")
131
+ assert !user.dirty?
132
+ end
133
+ end
134
+
135
+
136
+ context "when counting" do
137
+ setup do
138
+ recreate_db
139
+ end
140
+
141
+ context "when counting all" do
142
+ should "return the number of objects in the database" do
143
+ CountMe.create(:title => "Mr.")
144
+ CountMe.create(:title => "Mrs.")
145
+ assert_equal 2, CountMe.find(:all).size
146
+ assert_equal 2, CountMe.count
147
+ end
148
+
149
+ should "only count the correct class" do
150
+ CountMe.create(:title => "Mr.")
151
+ DontCountMe.create(:title => 'Foo')
152
+ assert_equal 1, CountMe.find(:all).size
153
+ assert_equal 1, CountMe.count
154
+ end
155
+ end
156
+
157
+ context "when counting by prefix" do
158
+ should "return the number of matching objects" do
159
+ CountMe.create(:title => "Mr.")
160
+ CountMe.create(:title => "Mrs.")
161
+ assert_equal 1, CountMe.find_all_by_title('Mr.').size
162
+ assert_equal 1, CountMe.count_by_title('Mr.')
163
+ end
164
+
165
+ should "only count the correct class" do
166
+ CountMe.create(:title => "Mr.")
167
+ DontCountMe.create(:title => 'Mr.')
168
+ assert_equal 1, CountMe.find_all_by_title('Mr.').size
169
+ assert_equal 1, CountMe.count_by_title('Mr.')
170
+ end
171
+ end
172
+ end
173
+
174
+ context "when reloading an instance" do
175
+ should "reload new attributes from the database" do
176
+ user = User.create(:title => "Mr.", :name => "Host Master")
177
+ user2 = User.find(user.id)
178
+ user2.update_attributes(:title => "Mrs.", :name => "Hostess Masteress")
179
+ user.reload
180
+ assert_equal "Mrs.", user.title
181
+ assert_equal "Hostess Masteress", user.name
182
+ end
183
+
184
+ should "remove attributes that are no longer in the database" do
185
+ user = User.create(:title => "Mr.", :name => "Host Master")
186
+ assert_not_nil user.name
187
+ same_user_in_different_thread = User.find(user.id)
188
+ same_user_in_different_thread.name = nil
189
+ same_user_in_different_thread.save!
190
+ assert_nil user.reload.name
191
+ end
192
+
193
+ should "also remove foreign key attributes that are no longer in the database" do
194
+ user = User.create(:title => "Mr.", :name => "Host Master")
195
+ post = Post.create(:user => user)
196
+ assert_not_nil post.user_id
197
+ same_post_in_different_thread = Post.find(post.id)
198
+ same_post_in_different_thread.user = nil
199
+ same_post_in_different_thread.save!
200
+ assert_nil post.reload.user_id
201
+ end
202
+
203
+ should "not be dirty after reloading" do
204
+ user = User.create(:title => "Mr.", :name => "Host Master")
205
+ user2 = User.find(user.id)
206
+ user2.update_attributes(:title => "Mrs.", :name => "Hostess Masteress")
207
+ user.reload
208
+ assert !user.dirty?
209
+ end
210
+
211
+ should "ensure that association caches for has_many are cleared" do
212
+ user = User.create(:title => "Mr.", :name => "Host Master")
213
+ post = Post.create(:user => user)
214
+ assert_equal 1, user.posts.size
215
+ assert_not_nil user.instance_variable_get("@posts")
216
+ user.reload
217
+ assert_nil user.instance_variable_get("@posts")
218
+ assert_not_nil user.posts.first
219
+ end
220
+
221
+ should "ensure that association caches for belongs_to are cleared" do
222
+ user = User.create(:title => "Mr.", :name => "Host Master")
223
+ post = Post.create(:user => user)
224
+ post.user
225
+ assert_not_nil post.instance_variable_get("@user")
226
+ post.reload
227
+ assert_nil post.instance_variable_get("@user")
228
+ assert_not_nil post.user
229
+ end
230
+
231
+ should "update the revision" do
232
+ user = User.create(:title => "Mr.", :name => "Host Master")
233
+ user2 = User.find(user.id)
234
+ user2.update_attributes(:title => "Mrs.", :name => "Hostess Masteress")
235
+ user.reload
236
+ assert_equal user._rev, user2._rev
237
+ end
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,77 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../fixtures/couch')
3
+
4
+ class CouchMassAssignmentProtectionTest < Test::Unit::TestCase
5
+ context "attribute proctection against mass assignment" do
6
+ setup do
7
+ CouchPotato::Config.database_name = 'simply_stored_test'
8
+ recreate_db
9
+ end
10
+
11
+ context "when using attr_protected" do
12
+ setup do
13
+ Category.instance_eval do
14
+ @_accessible_attributes = []
15
+ attr_protected :parent, :alias
16
+ end
17
+ end
18
+
19
+ should "not allow to set with mass assignment using attributes=" do
20
+ item = Category.new
21
+ item.attributes = {:parent => 'a', :name => 'c'}
22
+ assert_equal 'c', item.name
23
+ assert_nil item.parent
24
+ end
25
+
26
+ should "not allow to set with mass assignment using attributes= - ignore string vs. symbol" do
27
+ item = Category.new
28
+ item.attributes = {'parent' => 'a', 'name' => 'c'}
29
+ assert_equal 'c', item.name
30
+ assert_nil item.parent
31
+ end
32
+
33
+ should "not allow to set with mass assignment using the constructor" do
34
+ item = Category.new(:parent => 'a', :name => 'c')
35
+ assert_equal 'c', item.name
36
+ assert_nil item.parent
37
+ end
38
+
39
+ should "not allow to set with mass assignment using update_attributes" do
40
+ item = Category.new
41
+ item.update_attributes(:parent => 'a', :name => 'c')
42
+ assert_equal 'c', item.name
43
+ assert_nil item.parent
44
+ end
45
+ end
46
+
47
+ context "attr_accessible" do
48
+ setup do
49
+ Category.instance_eval do
50
+ @_protected_attributes = []
51
+ attr_accessible :name
52
+ end
53
+ end
54
+
55
+ should "not allow to set with mass assignment using attributes=" do
56
+ item = Category.new
57
+ item.attributes = {:parent => 'a', :name => 'c'}
58
+ assert_equal 'c', item.name
59
+ assert_nil item.parent
60
+ end
61
+
62
+ should "not allow to set with mass assignment using the constructor" do
63
+ item = Category.new(:parent => 'a', :name => 'c')
64
+ assert_equal 'c', item.name
65
+ assert_nil item.parent
66
+ end
67
+
68
+ should "not allow to set with mass assignment using update_attributes" do
69
+ item = Category.new
70
+ item.update_attributes(:parent => 'a', :name => 'c')
71
+ # item.reload
72
+ assert_equal 'c', item.name
73
+ assert_nil item.parent
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,256 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../fixtures/couch')
3
+
4
+ class CouchS3Test < Test::Unit::TestCase
5
+ context "with s3 interaction" do
6
+ setup do
7
+ CouchPotato::Config.database_name = 'simply_stored_test'
8
+ recreate_db
9
+ CouchLogItem.instance_variable_set(:@_s3_connection, nil)
10
+ CouchLogItem._s3_options[:log_data][:ca_file] = nil
11
+
12
+ bucket = stub(:bckt) do
13
+ stubs(:put).returns(true)
14
+ stubs(:get).returns(true)
15
+ end
16
+
17
+ @bucket = bucket
18
+
19
+ @s3 = stub(:s3) do
20
+ stubs(:bucket).returns(bucket)
21
+ end
22
+
23
+ RightAws::S3.stubs(:new).returns @s3
24
+ @log_item = CouchLogItem.new
25
+ end
26
+
27
+ context "when saving the attachment" do
28
+ should "fetch the collection" do
29
+ @log_item.log_data = "Yay! It logged!"
30
+ RightAws::S3.expects(:new).with('abcdef', 'secret!', :multi_thread => true, :ca_file => nil, :logger => nil).returns(@s3)
31
+ @log_item.save
32
+ end
33
+
34
+ should "upload the file" do
35
+ @log_item.log_data = "Yay! It logged!"
36
+ @bucket.expects(:put).with(anything, "Yay! It logged!", {}, anything)
37
+ @log_item.save
38
+ end
39
+
40
+ should "also upload on save!" do
41
+ @log_item.log_data = "Yay! It logged!"
42
+ @bucket.expects(:put).with(anything, "Yay! It logged!", {}, anything)
43
+ @log_item.save!
44
+ end
45
+
46
+ should "use the specified bucket" do
47
+ @log_item.log_data = "Yay! It logged!"
48
+ CouchLogItem._s3_options[:log_data][:bucket] = 'mybucket'
49
+ @s3.expects(:bucket).with('mybucket').returns(@bucket)
50
+ @log_item.save
51
+ end
52
+
53
+ should "create the bucket if it doesn't exist" do
54
+ @log_item.log_data = "Yay! log me"
55
+ CouchLogItem._s3_options[:log_data][:bucket] = 'mybucket'
56
+
57
+ @s3.expects(:bucket).with('mybucket').returns(nil)
58
+ @s3.expects(:bucket).with('mybucket', true, 'private', :location => nil).returns(@bucket)
59
+ @log_item.save
60
+ end
61
+
62
+ should "accept :us location option but not set it in RightAWS::S3" do
63
+ @log_item.log_data = "Yay! log me"
64
+ CouchLogItem._s3_options[:log_data][:bucket] = 'mybucket'
65
+ CouchLogItem._s3_options[:log_data][:location] = :us
66
+
67
+ @s3.expects(:bucket).with('mybucket').returns(nil)
68
+ @s3.expects(:bucket).with('mybucket', true, 'private', :location => nil).returns(@bucket)
69
+ @log_item.save
70
+ end
71
+
72
+ should "raise an error if the bucket is not ours" do
73
+ @log_item.log_data = "Yay! log me too"
74
+ CouchLogItem._s3_options[:log_data][:bucket] = 'mybucket'
75
+ CouchLogItem._s3_options[:log_data][:location] = :eu
76
+
77
+ @s3.expects(:bucket).with('mybucket').returns(nil)
78
+ @s3.expects(:bucket).with('mybucket', true, 'private', :location => :eu).raises(RightAws::AwsError, 'BucketAlreadyExists: The requested bucket name is not available. The bucket namespace is shared by all users of the system. Please select a different name and try again')
79
+
80
+ assert_raise(ArgumentError) do
81
+ @log_item.save
82
+ end
83
+ end
84
+
85
+ should "pass the logger object down to RightAws" do
86
+ logger = mock()
87
+ @log_item.log_data = "Yay! log me"
88
+ CouchLogItem._s3_options[:log_data][:bucket] = 'mybucket'
89
+ CouchLogItem._s3_options[:log_data][:logger] = logger
90
+
91
+ RightAws::S3.expects(:new).with(anything, anything, {:logger => logger, :ca_file => nil, :multi_thread => true}).returns(@s3)
92
+ @log_item.save
93
+ end
94
+
95
+ should "not upload the attachment when it hasn't been changed" do
96
+ @bucket.expects(:put).never
97
+ @log_item.save
98
+ end
99
+
100
+ should "set the permissions to private by default" do
101
+ class Item
102
+ include SimplyStored::Couch
103
+ has_s3_attachment :log_data, :bucket => 'mybucket'
104
+ end
105
+ @bucket.expects(:put).with(anything, anything, {}, 'private')
106
+ @log_item = Item.new
107
+ @log_item.log_data = 'Yay!'
108
+ @log_item.save
109
+ end
110
+
111
+ should "set the permissions to whatever's specified in the options for the attachment" do
112
+ @log_item.save
113
+ old_perms = CouchLogItem._s3_options[:log_data][:permissions]
114
+ CouchLogItem._s3_options[:log_data][:permissions] = 'public-read'
115
+ @bucket.expects(:put).with(anything, anything, {}, 'public-read')
116
+ @log_item.log_data = 'Yay!'
117
+ @log_item.save
118
+ CouchLogItem._s3_options[:log_data][:permissions] = old_perms
119
+ end
120
+
121
+ should "use the full class name and the id as key" do
122
+ @log_item.save
123
+ @bucket.expects(:put).with("couch_log_items/log_data/#{@log_item.id}", 'Yay!', {}, anything)
124
+ @log_item.log_data = 'Yay!'
125
+ @log_item.save
126
+ end
127
+
128
+ should "mark the attachment as not dirty after uploading" do
129
+ @log_item.log_data = 'Yay!'
130
+ @log_item.save
131
+ assert !@log_item.instance_variable_get(:@_s3_attachments)[:log_data][:dirty]
132
+ end
133
+
134
+ should 'store the attachment when the validations succeeded' do
135
+ @log_item.log_data = 'Yay!'
136
+ @log_item.stubs(:valid?).returns(true)
137
+ @bucket.expects(:put)
138
+ @log_item.save
139
+ end
140
+
141
+ should "not store the attachment when the validations failed" do
142
+ @log_item.log_data = 'Yay!'
143
+ @log_item.stubs(:valid?).returns(false)
144
+ @bucket.expects(:put).never
145
+ @log_item.save
146
+ end
147
+
148
+ should "save the attachment status" do
149
+ @log_item.save
150
+ @log_item.attributes["log_data_attachments"]
151
+ end
152
+
153
+ should "save generate the url for the attachment" do
154
+ @log_item._s3_options[:log_data][:bucket] = 'bucket-for-monsieur'
155
+ @log_item._s3_options[:log_data][:permissions] = 'public-read'
156
+ @log_item.save
157
+ assert_equal "http://bucket-for-monsieur.s3.amazonaws.com/#{@log_item.s3_attachment_key(:log_data)}", @log_item.log_data_url
158
+ end
159
+
160
+ should "add a short-lived access key for private attachments" do
161
+ @log_item._s3_options[:log_data][:bucket] = 'bucket-for-monsieur'
162
+ @log_item._s3_options[:log_data][:location] = :us
163
+ @log_item._s3_options[:log_data][:permissions] = 'private'
164
+ @log_item.save
165
+ assert @log_item.log_data_url.gsub("%2F", '/').include?("https://bucket-for-monsieur.s3.amazonaws.com:443/#{@log_item.s3_attachment_key(:log_data)}"), @log_item.log_data_url
166
+ assert @log_item.log_data_url.include?("Signature=")
167
+ assert @log_item.log_data_url.include?("Expires=")
168
+ end
169
+
170
+ should "serialize data other than strings to json" do
171
+ @log_item.log_data = ['one log entry', 'and another one']
172
+ @bucket.expects(:put).with(anything, '["one log entry","and another one"]', {}, anything)
173
+ @log_item.save
174
+ end
175
+
176
+ context "when noting the size of the attachment" do
177
+ should "store on upload" do
178
+ @log_item.log_data = 'abc'
179
+ @bucket.expects(:put)
180
+ assert @log_item.save
181
+ assert_equal 3, @log_item.log_data_size
182
+ end
183
+
184
+ should "update the size if the attachment gets updated" do
185
+ @log_item.log_data = 'abc'
186
+ @bucket.stubs(:put)
187
+ assert @log_item.save
188
+ assert_equal 3, @log_item.log_data_size
189
+
190
+ @log_item.log_data = 'example'
191
+ assert @log_item.save
192
+ assert_equal 7, @log_item.log_data_size
193
+ end
194
+
195
+ should "store the size of json attachments" do
196
+ @log_item.log_data = ['abc']
197
+ @bucket.stubs(:put)
198
+ assert @log_item.save
199
+ assert_equal ['abc'].to_json.size, @log_item.log_data_size
200
+ end
201
+ end
202
+ end
203
+
204
+ context "when fetching the data" do
205
+ should "create a configured S3 connection" do
206
+ CouchLogItem._s3_options[:log_data][:bucket] = 'mybucket'
207
+ CouchLogItem._s3_options[:log_data][:location] = :eu
208
+ CouchLogItem._s3_options[:log_data][:ca_file] = '/etc/ssl/ca.crt'
209
+
210
+ RightAws::S3.expects(:new).with('abcdef', 'secret!', :multi_thread => true, :ca_file => '/etc/ssl/ca.crt', :logger => nil).returns(@s3)
211
+
212
+ @log_item.log_data
213
+ end
214
+
215
+ should "fetch the data from s3 and set the attachment attribute" do
216
+ @log_item.instance_variable_set(:@_s3_attachments, {})
217
+ @bucket.expects(:get).with("couch_log_items/log_data/#{@log_item.id}").returns("Yay!")
218
+ assert_equal "Yay!", @log_item.log_data
219
+ end
220
+
221
+ should "not mark the the attachment as dirty" do
222
+ @log_item.instance_variable_set(:@_s3_attachments, {})
223
+ @bucket.expects(:get).with("couch_log_items/log_data/#{@log_item.id}").returns("Yay!")
224
+ @log_item.log_data
225
+ assert !@log_item._s3_attachments[:log_data][:dirty]
226
+ end
227
+
228
+ should "not try to fetch the attachment if the value is already set" do
229
+ @log_item.log_data = "Yay!"
230
+ @bucket.expects(:get).never
231
+ assert_equal "Yay!", @log_item.log_data
232
+ end
233
+ end
234
+
235
+ context "when deleting" do
236
+ setup do
237
+ CouchLogItem._s3_options[:log_data][:after_delete] = :nothing
238
+ @log_item.log_data = 'Yatzzee'
239
+ @log_item.save
240
+ end
241
+
242
+ should "do nothing to S3" do
243
+ @bucket.expects(:key).never
244
+ @log_item.delete
245
+ end
246
+
247
+ should "also delete on S3 if configured so" do
248
+ CouchLogItem._s3_options[:log_data][:after_delete] = :delete
249
+ s3_key = mock(:delete => true)
250
+ @bucket.expects(:key).with(@log_item.s3_attachment_key('log_data'), true).returns(s3_key)
251
+ @log_item.delete
252
+ end
253
+
254
+ end
255
+ end
256
+ end