mongoid-spec 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +373 -0
  4. data/Rakefile +17 -0
  5. data/lib/matchers/accept_nested_attributes.rb +67 -0
  6. data/lib/matchers/allow_mass_assignment.rb +102 -0
  7. data/lib/matchers/associations.rb +330 -0
  8. data/lib/matchers/be_dynamic_document.rb +26 -0
  9. data/lib/matchers/be_mongoid_document.rb +26 -0
  10. data/lib/matchers/be_stored_in.rb +50 -0
  11. data/lib/matchers/have_field.rb +90 -0
  12. data/lib/matchers/have_index_for.rb +63 -0
  13. data/lib/matchers/have_timestamps.rb +61 -0
  14. data/lib/matchers/validations.rb +81 -0
  15. data/lib/matchers/validations/acceptance_of.rb +9 -0
  16. data/lib/matchers/validations/associated.rb +19 -0
  17. data/lib/matchers/validations/confirmation_of.rb +22 -0
  18. data/lib/matchers/validations/custom_validation_of.rb +47 -0
  19. data/lib/matchers/validations/exclusion_of.rb +49 -0
  20. data/lib/matchers/validations/format_of.rb +71 -0
  21. data/lib/matchers/validations/inclusion_of.rb +49 -0
  22. data/lib/matchers/validations/length_of.rb +147 -0
  23. data/lib/matchers/validations/numericality_of.rb +90 -0
  24. data/lib/matchers/validations/presence_of.rb +9 -0
  25. data/lib/matchers/validations/uniqueness_of.rb +82 -0
  26. data/lib/matchers/validations/with_message.rb +27 -0
  27. data/lib/mongoid-rspec.rb +1 -0
  28. data/lib/mongoid/rspec.rb +40 -0
  29. data/lib/mongoid/rspec/version.rb +5 -0
  30. data/spec/models/article.rb +29 -0
  31. data/spec/models/comment.rb +6 -0
  32. data/spec/models/log.rb +9 -0
  33. data/spec/models/movie_article.rb +8 -0
  34. data/spec/models/permalink.rb +5 -0
  35. data/spec/models/person.rb +10 -0
  36. data/spec/models/profile.rb +16 -0
  37. data/spec/models/record.rb +5 -0
  38. data/spec/models/site.rb +9 -0
  39. data/spec/models/user.rb +37 -0
  40. data/spec/spec_helper.rb +35 -0
  41. data/spec/unit/accept_nested_attributes_spec.rb +12 -0
  42. data/spec/unit/associations_spec.rb +42 -0
  43. data/spec/unit/be_dynamic_document_spec.rb +22 -0
  44. data/spec/unit/be_mongoid_document_spec.rb +25 -0
  45. data/spec/unit/be_stored_in.rb +54 -0
  46. data/spec/unit/document_spec.rb +16 -0
  47. data/spec/unit/have_index_for_spec.rb +46 -0
  48. data/spec/unit/have_timestamps_spec.rb +71 -0
  49. data/spec/unit/validations_spec.rb +54 -0
  50. data/spec/validators/ssn_validator.rb +16 -0
  51. metadata +171 -0
@@ -0,0 +1,102 @@
1
+ # this code is totally extracted from shoulda-matchers gem.
2
+ module Mongoid
3
+ module Matchers
4
+ class AllowMassAssignmentOfMatcher # :nodoc:
5
+ attr_reader :failure_message, :negative_failure_message
6
+ alias :failure_message_when_negated :negative_failure_message
7
+
8
+ def initialize(attribute)
9
+ @attribute = attribute.to_s
10
+ @options = {}
11
+ end
12
+
13
+ def as(role)
14
+ if active_model_less_than_3_1?
15
+ raise "You can specify role only in Rails 3.1 or greater"
16
+ end
17
+ @options[:role] = role
18
+ self
19
+ end
20
+
21
+ def matches?(klass)
22
+ @klass = klass
23
+ if attr_mass_assignable?
24
+ if whitelisting?
25
+ @negative_failure_message = "#{@attribute} was made accessible"
26
+ else
27
+ if protected_attributes.empty?
28
+ @negative_failure_message = "no attributes were protected"
29
+ else
30
+ @negative_failure_message = "#{class_name} is protecting " <<
31
+ "#{protected_attributes.to_a.to_sentence}, " <<
32
+ "but not #{@attribute}."
33
+ end
34
+ end
35
+ true
36
+ else
37
+ if whitelisting?
38
+ @failure_message = "Expected #{@attribute} to be accessible"
39
+ else
40
+ @failure_message = "Did not expect #{@attribute} to be protected"
41
+ end
42
+ false
43
+ end
44
+ end
45
+
46
+ def description
47
+ "allow mass assignment of #{@attribute}"
48
+ end
49
+
50
+ private
51
+
52
+ def role
53
+ @options[:role] || :default
54
+ end
55
+
56
+ def protected_attributes
57
+ @protected_attributes ||= (@klass.class.protected_attributes || [])
58
+ end
59
+
60
+ def accessible_attributes
61
+ @accessible_attributes ||= (@klass.class.accessible_attributes || [])
62
+ end
63
+
64
+ def whitelisting?
65
+ authorizer.kind_of?(::ActiveModel::MassAssignmentSecurity::WhiteList)
66
+ end
67
+
68
+ def attr_mass_assignable?
69
+ !authorizer.deny?(@attribute)
70
+ end
71
+
72
+ def authorizer
73
+ if active_model_less_than_3_1?
74
+ @klass.class.active_authorizer
75
+ else
76
+ @klass.class.active_authorizer[role]
77
+ end
78
+ end
79
+
80
+ def class_name
81
+ @klass.class.name
82
+ end
83
+
84
+ def active_model_less_than_3_1?
85
+ ::ActiveModel::VERSION::STRING.to_f < 3.1
86
+ end
87
+ end
88
+
89
+ # Ensures that the attribute can be set on mass update.
90
+ #
91
+ # it { should_not allow_mass_assignment_of(:password) }
92
+ # it { should allow_mass_assignment_of(:first_name) }
93
+ #
94
+ # In Rails 3.1 you can check role as well:
95
+ #
96
+ # it { should allow_mass_assignment_of(:first_name).as(:admin) }
97
+ #
98
+ def allow_mass_assignment_of(value)
99
+ AllowMassAssignmentOfMatcher.new(value)
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,330 @@
1
+ require 'mongoid/relations'
2
+
3
+ module Mongoid
4
+ module Matchers
5
+ module Associations
6
+
7
+ HAS_MANY = Mongoid::Relations::Referenced::Many
8
+ HAS_AND_BELONGS_TO_MANY = Mongoid::Relations::Referenced::ManyToMany
9
+ HAS_ONE = Mongoid::Relations::Referenced::One
10
+ BELONGS_TO = Mongoid::Relations::Referenced::In
11
+ EMBEDS_MANY = Mongoid::Relations::Embedded::Many
12
+ EMBEDS_ONE = Mongoid::Relations::Embedded::One
13
+ EMBEDDED_IN = Mongoid::Relations::Embedded::In
14
+
15
+ class HaveAssociationMatcher
16
+ def initialize(name, association_type)
17
+ @association = {}
18
+ @association[:name] = name.to_s
19
+ @association[:type] = association_type
20
+ @expectation_message = "#{type_description} #{@association[:name].inspect}"
21
+ @expectation_message << " of type #{@association[:class].inspect}" unless @association[:class].nil?
22
+ end
23
+
24
+ def of_type(klass)
25
+ @association[:class] = klass
26
+ @expectation_message << " of type #{@association[:class].inspect}"
27
+ self
28
+ end
29
+
30
+ def as_inverse_of(association_inverse_name)
31
+ @association[:inverse_of] = association_inverse_name.to_s
32
+ @expectation_message << " which is an inverse of #{@association[:inverse_of].inspect}"
33
+ self
34
+ end
35
+
36
+ def ordered_by(association_field_name)
37
+ raise "#{@association[:type].inspect} does not respond to :order" unless [HAS_MANY, HAS_AND_BELONGS_TO_MANY, EMBEDS_MANY].include?(@association[:type])
38
+ @association[:order] = association_field_name.to_s
39
+ @expectation_message << " ordered by #{@association[:order].inspect}"
40
+
41
+ if association_field_name.is_a? Mongoid::Criteria::Queryable::Key
42
+ @association[:order_operator] = association_field_name.operator
43
+ @expectation_message << " #{order_way(@association[:order_operator])}"
44
+ end
45
+
46
+ self
47
+ end
48
+
49
+ def with_dependent(method_name)
50
+ @association[:dependent] = method_name
51
+ @expectation_message << " which specifies dependent as #{@association[:dependent].to_s}"
52
+ self
53
+ end
54
+
55
+ def with_autosave
56
+ @association[:autosave] = true
57
+ @expectation_message << " which specifies autosave as #{@association[:autosave].to_s}"
58
+ self
59
+ end
60
+
61
+ def with_index
62
+ @association[:index] = true
63
+ @expectation_message << " which specifies index as #{@association[:index].to_s}"
64
+ self
65
+ end
66
+
67
+ def with_autobuild
68
+ @association[:autobuild] = true
69
+ @expectation_message << " which specifies autobuild as #{@association[:autobuild].to_s}"
70
+ self
71
+ end
72
+
73
+ def with_polymorphism
74
+ @association[:polymorphic] = true
75
+ @expectation_message << " which specifies polymorphic as #{@association[:polymorphic].to_s}"
76
+ self
77
+ end
78
+
79
+ def with_cascading_callbacks
80
+ @association[:cascade_callbacks] = true
81
+ @expectation_message << " which specifies cascade_callbacks as #{@association[:cascade_callbacks].to_s}"
82
+ self
83
+ end
84
+
85
+ def cyclic
86
+ @association[:cyclic] = true
87
+ @expectation_message << " which specifies cyclic as #{@association[:cyclic].to_s}"
88
+ self
89
+ end
90
+
91
+ def stored_as(store_as)
92
+ @association[:store_as] = store_as.to_s
93
+ @expectation_message << " which is stored as #{@association[:store_as].inspect}"
94
+ self
95
+ end
96
+
97
+ def with_foreign_key(foreign_key)
98
+ @association[:foreign_key] = foreign_key.to_s
99
+ @expectation_message << " using foreign key #{@association[:foreign_key].inspect}"
100
+ self
101
+ end
102
+
103
+ def with_counter_cache
104
+ @association[:counter_cache] = true
105
+ @expectation_message << " which specifies counter_cache as #{@association[:counter_cache].to_s}"
106
+ self
107
+ end
108
+
109
+ def matches?(actual)
110
+ @actual = actual.is_a?(Class) ? actual : actual.class
111
+ metadata = @actual.relations[@association[:name]]
112
+
113
+ if metadata.nil?
114
+ @negative_result_message = "no association named #{@association[:name]}"
115
+ return false
116
+ else
117
+ @positive_result_message = "association named #{@association[:name]}"
118
+ end
119
+
120
+ relation = metadata.relation
121
+ if relation != @association[:type]
122
+ @negative_result_message = "#{@actual.inspect} #{type_description(relation, false)} #{@association[:name]}"
123
+ return false
124
+ else
125
+ @positive_result_message = "#{@actual.inspect} #{type_description(relation, false)} #{@association[:name]}"
126
+ end
127
+
128
+ if !@association[:class].nil? and @association[:class] != metadata.klass
129
+ @negative_result_message = "#{@positive_result_message} of type #{metadata.klass.inspect}"
130
+ return false
131
+ else
132
+ @positive_result_message = "#{@positive_result_message}#{" of type #{metadata.klass.inspect}" if @association[:class]}"
133
+ end
134
+
135
+ if @association[:inverse_of]
136
+ if @association[:inverse_of].to_s != metadata.inverse_of.to_s
137
+ @negative_result_message = "#{@positive_result_message} which is an inverse of #{metadata.inverse_of}"
138
+ return false
139
+ else
140
+ @positive_result_message = "#{@positive_result_message} which is an inverse of #{metadata.inverse_of}"
141
+ end
142
+ end
143
+
144
+ if @association[:order]
145
+ if @association[:order].to_s != metadata.order.to_s
146
+ @negative_result_message = "#{@positive_result_message} ordered by #{metadata.order}"
147
+ return false
148
+ else
149
+ @positive_result_message = "#{@positive_result_message} ordered by #{metadata.order}"
150
+ end
151
+ end
152
+
153
+ if @association[:order_operator]
154
+ if @association[:order_operator] != metadata.order.operator
155
+ @negative_result_message = "#{@positive_result_message} #{order_way(@association[:order_operator] * -1)}"
156
+ return false
157
+ else
158
+ @positive_result_message = "#{@positive_result_message} #{order_way(@association[:order_operator])}"
159
+ end
160
+ end
161
+
162
+ if @association[:dependent]
163
+ if @association[:dependent].to_s != metadata.dependent.to_s
164
+ @negative_result_message = "#{@positive_result_message} which specified dependent as #{metadata.dependent}"
165
+ return false
166
+ else
167
+ @positive_result_message = "#{@positive_result_message} which specified dependent as #{metadata.dependent}"
168
+ end
169
+ end
170
+
171
+ if @association[:autosave]
172
+ if metadata.autosave != true
173
+ @negative_result_message = "#{@positive_result_message} which did not set autosave"
174
+ return false
175
+ else
176
+ @positive_result_message = "#{@positive_result_message} which set autosave"
177
+ end
178
+ end
179
+
180
+ if @association[:autobuild]
181
+ if metadata.autobuilding? != true
182
+ @negative_result_message = "#{@positive_result_message} which did not set autobuild"
183
+ return false
184
+ else
185
+ @positive_result_message = "#{@positive_result_message} which set autobuild"
186
+ end
187
+ end
188
+
189
+ if @association[:polymorphic]
190
+ if metadata.polymorphic? != true
191
+ @negative_result_message = "#{@positive_result_message} which did not set polymorphic"
192
+ return false
193
+ else
194
+ @positive_result_message = "#{@positive_result_message} which set polymorphic"
195
+ end
196
+ end
197
+
198
+ if @association[:cascade_callbacks]
199
+ if metadata.cascading_callbacks? != true
200
+ @negative_result_message = "#{@positive_result_message} which did not set cascade_callbacks"
201
+ return false
202
+ else
203
+ @positive_result_message = "#{@positive_result_message} which set cascade_callbacks"
204
+ end
205
+ end
206
+
207
+ if @association[:cyclic]
208
+ if metadata.cyclic? != true
209
+ @negative_result_message = "#{@positive_result_message} which did not set cyclic"
210
+ return false
211
+ else
212
+ @positive_result_message = "#{@positive_result_message} which set cyclic"
213
+ end
214
+ end
215
+
216
+ if @association[:store_as]
217
+ if metadata.store_as != @association[:store_as]
218
+ @negative_result_message = "#{@positive_result_message} which is stored as #{metadata.store_as}"
219
+ return false
220
+ else
221
+ @positive_result_message = "#{@positive_result_message} which is stored as #{metadata.store_as}"
222
+ end
223
+ end
224
+
225
+ if @association[:index]
226
+ if metadata.index != true
227
+ @negative_result_message = "#{@positive_result_message} which did not set index"
228
+ return false
229
+ else
230
+ @positive_result_message = "#{@positive_result_message} which set index"
231
+ end
232
+ end
233
+
234
+ if @association[:foreign_key]
235
+ if metadata.foreign_key != @association[:foreign_key]
236
+ @negative_result_message = "#{@positive_result_message} with foreign key #{metadata.foreign_key.inspect}"
237
+ return false
238
+ else
239
+ @positive_result_message = "#{@positive_result_message} with foreign key #{metadata.foreign_key.inspect}"
240
+ end
241
+ end
242
+
243
+ if @association[:counter_cache]
244
+ if metadata.counter_cached? != true
245
+ @negative_result_message = "#{@positive_result_message} which did not set counter_cache"
246
+ return false
247
+ else
248
+ @positive_result_message = "#{@positive_result_message} which set counter_cache"
249
+ end
250
+ end
251
+
252
+ return true
253
+ end
254
+
255
+ def failure_message_for_should
256
+ "Expected #{@actual.inspect} to #{@expectation_message}, got #{@negative_result_message}"
257
+ end
258
+
259
+ def failure_message_for_should_not
260
+ "Expected #{@actual.inspect} to not #{@expectation_message}, got #{@positive_result_message}"
261
+ end
262
+
263
+ alias :failure_message :failure_message_for_should
264
+ alias :failure_message_when_negated :failure_message_for_should_not
265
+
266
+ def description
267
+ @expectation_message
268
+ end
269
+
270
+ def type_description(type = nil, passive = true)
271
+ type ||= @association[:type]
272
+ case type.name
273
+ when EMBEDS_ONE.name
274
+ (passive ? 'embed' : 'embeds') << ' one'
275
+ when EMBEDS_MANY.name
276
+ (passive ? 'embed' : 'embeds') << ' many'
277
+ when EMBEDDED_IN.name
278
+ (passive ? 'be' : 'is') << ' embedded in'
279
+ when HAS_ONE.name
280
+ (passive ? 'reference' : 'references') << ' one'
281
+ when HAS_MANY.name
282
+ (passive ? 'reference' : 'references') << ' many'
283
+ when HAS_AND_BELONGS_TO_MANY.name
284
+ (passive ? 'reference' : 'references') << ' and referenced in many'
285
+ when BELONGS_TO.name
286
+ (passive ? 'be referenced in' : 'referenced in')
287
+ else
288
+ raise "Unknown association type '%s'" % type
289
+ end
290
+ end
291
+
292
+ private
293
+ def order_way(operator)
294
+ [nil, "ascending", "descending"][operator]
295
+ end
296
+ end
297
+
298
+ def embed_one(association_name)
299
+ HaveAssociationMatcher.new(association_name, EMBEDS_ONE)
300
+ end
301
+
302
+ def embed_many(association_name)
303
+ HaveAssociationMatcher.new(association_name, EMBEDS_MANY)
304
+ end
305
+
306
+ def be_embedded_in(association_name)
307
+ HaveAssociationMatcher.new(association_name, EMBEDDED_IN)
308
+ end
309
+
310
+ def have_one_related(association_name)
311
+ HaveAssociationMatcher.new(association_name, HAS_ONE)
312
+ end
313
+ alias :have_one :have_one_related
314
+
315
+ def have_many_related(association_name)
316
+ HaveAssociationMatcher.new(association_name, HAS_MANY)
317
+ end
318
+ alias :have_many :have_many_related
319
+
320
+ def have_and_belong_to_many(association_name)
321
+ HaveAssociationMatcher.new(association_name, HAS_AND_BELONGS_TO_MANY)
322
+ end
323
+
324
+ def belong_to_related(association_name)
325
+ HaveAssociationMatcher.new(association_name, BELONGS_TO)
326
+ end
327
+ alias :belong_to :belong_to_related
328
+ end
329
+ end
330
+ end
@@ -0,0 +1,26 @@
1
+ module Mongoid
2
+ module Matchers
3
+ def be_dynamic_document
4
+ BeDynamicDocument.new
5
+ end
6
+
7
+ class BeDynamicDocument
8
+ def matches?(actual)
9
+ @model = actual.is_a?(Class) ? actual : actual.class
10
+ @model.included_modules.include?(Mongoid::Attributes::Dynamic)
11
+ end
12
+
13
+ def description
14
+ 'include Mongoid::Attributes::Dynamic'
15
+ end
16
+
17
+ def failure_message
18
+ "expect #{@model.inspect} class to #{description}"
19
+ end
20
+
21
+ def failure_message_when_negated
22
+ "expect #{@model.inspect} class to not #{description}"
23
+ end
24
+ end
25
+ end
26
+ end