glebtv-mongoid-rspec 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitignore +6 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +14 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE +20 -0
  9. data/README.md +203 -0
  10. data/Rakefile +18 -0
  11. data/gemfiles/mongoid-3.1.gemfile +5 -0
  12. data/gemfiles/mongoid-4.0.gemfile +5 -0
  13. data/glebtv-mongoid-rspec.gemspec +25 -0
  14. data/lib/glebtv-mongoid-rspec.rb +1 -0
  15. data/lib/matchers/accept_nested_attributes.rb +65 -0
  16. data/lib/matchers/allow_mass_assignment.rb +101 -0
  17. data/lib/matchers/associations.rb +313 -0
  18. data/lib/matchers/collections.rb +9 -0
  19. data/lib/matchers/document.rb +160 -0
  20. data/lib/matchers/indexes.rb +81 -0
  21. data/lib/matchers/validations.rb +78 -0
  22. data/lib/matchers/validations/acceptance_of.rb +9 -0
  23. data/lib/matchers/validations/associated.rb +19 -0
  24. data/lib/matchers/validations/confirmation_of.rb +9 -0
  25. data/lib/matchers/validations/custom_validation_of.rb +47 -0
  26. data/lib/matchers/validations/exclusion_of.rb +49 -0
  27. data/lib/matchers/validations/format_of.rb +71 -0
  28. data/lib/matchers/validations/inclusion_of.rb +49 -0
  29. data/lib/matchers/validations/length_of.rb +147 -0
  30. data/lib/matchers/validations/numericality_of.rb +74 -0
  31. data/lib/matchers/validations/presence_of.rb +9 -0
  32. data/lib/matchers/validations/uniqueness_of.rb +82 -0
  33. data/lib/matchers/validations/with_message.rb +27 -0
  34. data/lib/mongoid-rspec.rb +33 -0
  35. data/lib/mongoid-rspec/version.rb +5 -0
  36. data/spec/models/article.rb +29 -0
  37. data/spec/models/comment.rb +6 -0
  38. data/spec/models/log.rb +4 -0
  39. data/spec/models/movie_article.rb +8 -0
  40. data/spec/models/permalink.rb +5 -0
  41. data/spec/models/person.rb +10 -0
  42. data/spec/models/profile.rb +16 -0
  43. data/spec/models/record.rb +5 -0
  44. data/spec/models/site.rb +9 -0
  45. data/spec/models/user.rb +36 -0
  46. data/spec/spec_helper.rb +34 -0
  47. data/spec/unit/accept_nested_attributes_spec.rb +12 -0
  48. data/spec/unit/associations_spec.rb +42 -0
  49. data/spec/unit/collections_spec.rb +7 -0
  50. data/spec/unit/document_spec.rb +27 -0
  51. data/spec/unit/indexes_spec.rb +13 -0
  52. data/spec/unit/validations_spec.rb +52 -0
  53. data/spec/validators/ssn_validator.rb +16 -0
  54. metadata +163 -0
@@ -0,0 +1,313 @@
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
+ raise "#{@association[:type].inspect} does not respond to :inverse_of" unless [HAS_MANY, HAS_AND_BELONGS_TO_MANY, BELONGS_TO, EMBEDDED_IN].include?(@association[:type])
32
+ @association[:inverse_of] = association_inverse_name.to_s
33
+ @expectation_message << " which is an inverse of #{@association[:inverse_of].inspect}"
34
+ self
35
+ end
36
+
37
+ def ordered_by(association_field_name)
38
+ raise "#{@association[:type].inspect} does not respond to :order" unless [HAS_MANY, HAS_AND_BELONGS_TO_MANY, EMBEDS_MANY].include?(@association[:type])
39
+ @association[:order] = association_field_name.to_s
40
+ @expectation_message << " ordered by #{@association[:order].inspect}"
41
+
42
+ if association_field_name.is_a? Origin::Key
43
+ @association[:order_operator] = association_field_name.operator
44
+ @expectation_message << " #{order_way(@association[:order_operator])}"
45
+ end
46
+
47
+ self
48
+ end
49
+
50
+ def with_dependent(method_name)
51
+ @association[:dependent] = method_name
52
+ @expectation_message << " which specifies dependent as #{@association[:dependent].to_s}"
53
+ self
54
+ end
55
+
56
+ def with_autosave
57
+ @association[:autosave] = true
58
+ @expectation_message << " which specifies autosave as #{@association[:autosave].to_s}"
59
+ self
60
+ end
61
+
62
+ def with_index
63
+ @association[:index] = true
64
+ @expectation_message << " which specifies index as #{@association[:index].to_s}"
65
+ self
66
+ end
67
+
68
+ def with_autobuild
69
+ @association[:autobuild] = true
70
+ @expectation_message << " which specifies autobuild as #{@association[:autobuild].to_s}"
71
+ self
72
+ end
73
+
74
+ def with_polymorphism
75
+ @association[:polymorphic] = true
76
+ @expectation_message << " which specifies polymorphic as #{@association[:polymorphic].to_s}"
77
+ self
78
+ end
79
+
80
+ def with_cascading_callbacks
81
+ @association[:cascade_callbacks] = true
82
+ @expectation_message << " which specifies cascade_callbacks as #{@association[:cascade_callbacks].to_s}"
83
+ self
84
+ end
85
+
86
+ def cyclic
87
+ @association[:cyclic] = true
88
+ @expectation_message << " which specifies cyclic as #{@association[:cyclic].to_s}"
89
+ self
90
+ end
91
+
92
+ def stored_as(store_as)
93
+ @association[:store_as] = store_as.to_s
94
+ @expectation_message << " which is stored as #{@association[:store_as].inspect}"
95
+ self
96
+ end
97
+
98
+ def with_foreign_key(foreign_key)
99
+ @association[:foreign_key] = foreign_key.to_s
100
+ @expectation_message << " using foreign key #{@association[:foreign_key].inspect}"
101
+ self
102
+ end
103
+
104
+ def matches?(actual)
105
+ @actual = actual.is_a?(Class) ? actual : actual.class
106
+ metadata = @actual.relations[@association[:name]]
107
+
108
+ if metadata.nil?
109
+ @negative_result_message = "no association named #{@association[:name]}"
110
+ return false
111
+ else
112
+ @positive_result_message = "association named #{@association[:name]}"
113
+ end
114
+
115
+ relation = metadata.relation
116
+ if relation != @association[:type]
117
+ @negative_result_message = "#{@actual.inspect} #{type_description(relation, false)} #{@association[:name]}"
118
+ return false
119
+ else
120
+ @positive_result_message = "#{@actual.inspect} #{type_description(relation, false)} #{@association[:name]}"
121
+ end
122
+
123
+ if !@association[:class].nil? and @association[:class] != metadata.klass
124
+ @negative_result_message = "#{@positive_result_message} of type #{metadata.klass.inspect}"
125
+ return false
126
+ else
127
+ @positive_result_message = "#{@positive_result_message}#{" of type #{metadata.klass.inspect}" if @association[:class]}"
128
+ end
129
+
130
+ if @association[:inverse_of]
131
+ if @association[:inverse_of].to_s != metadata.inverse_of.to_s
132
+ @negative_result_message = "#{@positive_result_message} which is an inverse of #{metadata.inverse_of}"
133
+ return false
134
+ else
135
+ @positive_result_message = "#{@positive_result_message} which is an inverse of #{metadata.inverse_of}"
136
+ end
137
+ end
138
+
139
+ if @association[:order]
140
+ if @association[:order].to_s != metadata.order.to_s
141
+ @negative_result_message = "#{@positive_result_message} ordered by #{metadata.order}"
142
+ return false
143
+ else
144
+ @positive_result_message = "#{@positive_result_message} ordered by #{metadata.order}"
145
+ end
146
+ end
147
+
148
+ if @association[:order_operator]
149
+ if @association[:order_operator] != metadata.order.operator
150
+ @negative_result_message = "#{@positive_result_message} #{order_way(@association[:order_operator] * -1)}"
151
+ return false
152
+ else
153
+ @positive_result_message = "#{@positive_result_message} #{order_way(@association[:order_operator])}"
154
+ end
155
+ end
156
+
157
+ if @association[:dependent]
158
+ if @association[:dependent].to_s != metadata.dependent.to_s
159
+ @negative_result_message = "#{@positive_result_message} which specified dependent as #{metadata.dependent}"
160
+ return false
161
+ else
162
+ @positive_result_message = "#{@positive_result_message} which specified dependent as #{metadata.dependent}"
163
+ end
164
+ end
165
+
166
+ if @association[:autosave]
167
+ if metadata.autosave != true
168
+ @negative_result_message = "#{@positive_result_message} which did not set autosave"
169
+ return false
170
+ else
171
+ @positive_result_message = "#{@positive_result_message} which set autosave"
172
+ end
173
+ end
174
+
175
+ if @association[:autobuild]
176
+ if metadata.autobuilding? != true
177
+ @negative_result_message = "#{@positive_result_message} which did not set autobuild"
178
+ return false
179
+ else
180
+ @positive_result_message = "#{@positive_result_message} which set autobuild"
181
+ end
182
+ end
183
+
184
+ if @association[:polymorphic]
185
+ if metadata.polymorphic? != true
186
+ @negative_result_message = "#{@positive_result_message} which did not set polymorphic"
187
+ return false
188
+ else
189
+ @positive_result_message = "#{@positive_result_message} which set polymorphic"
190
+ end
191
+ end
192
+
193
+ if @association[:cascade_callbacks]
194
+ if metadata.cascading_callbacks? != true
195
+ @negative_result_message = "#{@positive_result_message} which did not set cascade_callbacks"
196
+ return false
197
+ else
198
+ @positive_result_message = "#{@positive_result_message} which set cascade_callbacks"
199
+ end
200
+ end
201
+
202
+ if @association[:cyclic]
203
+ if metadata.cyclic? != true
204
+ @negative_result_message = "#{@positive_result_message} which did not set cyclic"
205
+ return false
206
+ else
207
+ @positive_result_message = "#{@positive_result_message} which set cyclic"
208
+ end
209
+ end
210
+
211
+ if @association[:store_as]
212
+ if metadata.store_as != @association[:store_as]
213
+ @negative_result_message = "#{@positive_result_message} which is stored as #{metadata.store_as}"
214
+ return false
215
+ else
216
+ @positive_result_message = "#{@positive_result_message} which is stored as #{metadata.store_as}"
217
+ end
218
+ end
219
+
220
+ if @association[:index]
221
+ if metadata.index != true
222
+ @negative_result_message = "#{@positive_result_message} which did not set index"
223
+ return false
224
+ else
225
+ @positive_result_message = "#{@positive_result_message} which set index"
226
+ end
227
+ end
228
+
229
+ if @association[:foreign_key]
230
+ if metadata.foreign_key != @association[:foreign_key]
231
+ @negative_result_message = "#{@positive_result_message} with foreign key #{metadata.foreign_key.inspect}"
232
+ return false
233
+ else
234
+ @positive_result_message = "#{@positive_result_message} with foreign key #{metadata.foreign_key.inspect}"
235
+ end
236
+ end
237
+
238
+ return true
239
+ end
240
+
241
+ def failure_message_for_should
242
+ "Expected #{@actual.inspect} to #{@expectation_message}, got #{@negative_result_message}"
243
+ end
244
+
245
+ def failure_message_for_should_not
246
+ "Expected #{@actual.inspect} to not #{@expectation_message}, got #{@positive_result_message}"
247
+ end
248
+
249
+ def description
250
+ @expectation_message
251
+ end
252
+
253
+ def type_description(type = nil, passive = true)
254
+ type ||= @association[:type]
255
+ case type.name
256
+ when EMBEDS_ONE.name
257
+ (passive ? 'embed' : 'embeds') << ' one'
258
+ when EMBEDS_MANY.name
259
+ (passive ? 'embed' : 'embeds') << ' many'
260
+ when EMBEDDED_IN.name
261
+ (passive ? 'be' : 'is') << ' embedded in'
262
+ when HAS_ONE.name
263
+ (passive ? 'reference' : 'references') << ' one'
264
+ when HAS_MANY.name
265
+ (passive ? 'reference' : 'references') << ' many'
266
+ when HAS_AND_BELONGS_TO_MANY.name
267
+ (passive ? 'reference' : 'references') << ' and referenced in many'
268
+ when BELONGS_TO.name
269
+ (passive ? 'be referenced in' : 'referenced in')
270
+ else
271
+ raise "Unknown association type '%s'" % type
272
+ end
273
+ end
274
+
275
+ private
276
+ def order_way(operator)
277
+ [nil, "ascending", "descending"][operator]
278
+ end
279
+ end
280
+
281
+ def embed_one(association_name)
282
+ HaveAssociationMatcher.new(association_name, EMBEDS_ONE)
283
+ end
284
+
285
+ def embed_many(association_name)
286
+ HaveAssociationMatcher.new(association_name, EMBEDS_MANY)
287
+ end
288
+
289
+ def be_embedded_in(association_name)
290
+ HaveAssociationMatcher.new(association_name, EMBEDDED_IN)
291
+ end
292
+
293
+ def have_one_related(association_name)
294
+ HaveAssociationMatcher.new(association_name, HAS_ONE)
295
+ end
296
+ alias :have_one :have_one_related
297
+
298
+ def have_many_related(association_name)
299
+ HaveAssociationMatcher.new(association_name, HAS_MANY)
300
+ end
301
+ alias :have_many :have_many_related
302
+
303
+ def have_and_belong_to_many(association_name)
304
+ HaveAssociationMatcher.new(association_name, HAS_AND_BELONGS_TO_MANY)
305
+ end
306
+
307
+ def belong_to_related(association_name)
308
+ HaveAssociationMatcher.new(association_name, BELONGS_TO)
309
+ end
310
+ alias :belong_to :belong_to_related
311
+ end
312
+ end
313
+ end
@@ -0,0 +1,9 @@
1
+ RSpec::Matchers.define :be_stored_in do |collection_name|
2
+ match do |doc|
3
+ doc.class.collection_name == collection_name
4
+ end
5
+
6
+ description do
7
+ "be stored in #{collection_name.to_s}"
8
+ end
9
+ end
@@ -0,0 +1,160 @@
1
+ module Mongoid
2
+ module Matchers
3
+ class HaveFieldMatcher # :nodoc:
4
+ def initialize(*attrs)
5
+ @attributes = attrs.collect(&:to_s)
6
+ end
7
+
8
+ def localized
9
+ @localized = true
10
+ self
11
+ end
12
+
13
+ def of_type(type)
14
+ @type = type
15
+ self
16
+ end
17
+
18
+ def with_alias(field_alias)
19
+ @field_alias = field_alias
20
+ self
21
+ end
22
+
23
+ def with_default_value_of(default)
24
+ @default = default
25
+ self
26
+ end
27
+
28
+ def matches?(klass)
29
+ @klass = klass.is_a?(Class) ? klass : klass.class
30
+ @errors = []
31
+ @attributes.each do |attr|
32
+ if @klass.fields.include?(attr)
33
+ error = ""
34
+ if @type and @klass.fields[attr].type != @type
35
+ error << " of type #{@klass.fields[attr].type}"
36
+ end
37
+
38
+ if !@default.nil?
39
+ if @klass.fields[attr].default_val.nil?
40
+ error << " with default not set"
41
+ elsif @klass.fields[attr].default_val != @default
42
+ error << " with default value of #{@klass.fields[attr].default_val}"
43
+ end
44
+ end
45
+
46
+ if @field_alias and @klass.fields[attr].options[:as] != @field_alias
47
+ error << " with alias #{@klass.fields[attr].options[:as]}"
48
+ end
49
+
50
+ @errors.push("field #{attr.inspect}" << error) unless error.blank?
51
+
52
+ if @localized
53
+ unless @klass.fields[attr].localized?
54
+ @errors.push "is not localized #{attr.inspect}"
55
+ end
56
+ end
57
+
58
+ else
59
+ @errors.push "no field named #{attr.inspect}"
60
+ end
61
+ end
62
+ @errors.empty?
63
+ end
64
+
65
+ def failure_message_for_should
66
+ "Expected #{@klass.inspect} to #{description}, got #{@errors.to_sentence}"
67
+ end
68
+
69
+ def failure_message_for_should_not
70
+ "Expected #{@klass.inspect} to not #{description}, got #{@klass.inspect} to #{description}"
71
+ end
72
+
73
+ def description
74
+ desc = "have #{@attributes.size > 1 ? 'fields' : 'field'} named #{@attributes.collect(&:inspect).to_sentence}"
75
+ desc << " of type #{@type.inspect}" if @type
76
+ desc << " with alias #{@field_alias}" if @field_alias
77
+ desc << " with default value of #{@default.inspect}" if !@default.nil?
78
+ desc
79
+ end
80
+ end
81
+
82
+ def have_field(*args)
83
+ HaveFieldMatcher.new(*args)
84
+ end
85
+ alias_method :have_fields, :have_field
86
+ end
87
+ end
88
+
89
+ RSpec::Matchers.define :have_instance_method do |name|
90
+ match do |klass|
91
+ klass.instance_methods.include?(name.to_sym)
92
+ end
93
+
94
+ description do
95
+ "have instance method #{name.to_s}"
96
+ end
97
+ end
98
+
99
+ RSpec::Matchers.define :be_mongoid_document do
100
+ match do |doc|
101
+ doc.class.included_modules.include?(Mongoid::Document)
102
+ end
103
+
104
+ description do
105
+ "be a Mongoid document"
106
+ end
107
+ end
108
+
109
+ RSpec::Matchers.define :be_versioned_document do
110
+ match do |doc|
111
+ doc.class.included_modules.include?(Mongoid::Versioning)
112
+ end
113
+
114
+ description do
115
+ "be a versioned Mongoid document"
116
+ end
117
+ end
118
+
119
+ RSpec::Matchers.define :be_timestamped_document do
120
+ match do |doc|
121
+ if [*@timestamped_module].any?
122
+ modules = [*@timestamped_module].map{|m| "Mongoid::Timestamps::#{m.to_s.classify}".constantize }
123
+ (modules - doc.class.included_modules).empty?
124
+ else
125
+ doc.class.included_modules.include?(Mongoid::Timestamps) or
126
+ doc.class.included_modules.include?(Mongoid::Timestamps::Created) or
127
+ doc.class.included_modules.include?(Mongoid::Timestamps::Updated)
128
+ end
129
+ end
130
+
131
+ chain :with do |timestamped_module|
132
+ @timestamped_module = timestamped_module
133
+ end
134
+
135
+ description do
136
+ desc = "be a timestamped Mongoid document"
137
+ desc << " with #{@timestamped_module}" if @timestamped_module
138
+ desc
139
+ end
140
+ end
141
+
142
+ RSpec::Matchers.define :be_paranoid_document do
143
+ match do |doc|
144
+ doc.class.included_modules.include?(Mongoid::Paranoia)
145
+ end
146
+
147
+ description do
148
+ "be a paranoid Mongoid document"
149
+ end
150
+ end
151
+
152
+ RSpec::Matchers.define :be_multiparameted_document do
153
+ match do |doc|
154
+ doc.class.included_modules.include?(Mongoid::MultiParameterAttributes)
155
+ end
156
+
157
+ description do
158
+ "be a multiparameted Mongoid document"
159
+ end
160
+ end