shoulda-matchers 2.8.0 → 3.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (171) hide show
  1. checksums.yaml +4 -4
  2. data/.hound_config/ruby.yml +7 -0
  3. data/.travis.yml +11 -54
  4. data/Appraisals +45 -100
  5. data/CONTRIBUTING.md +51 -7
  6. data/Gemfile +7 -19
  7. data/Gemfile.lock +60 -134
  8. data/Guardfile +5 -0
  9. data/NEWS.md +203 -0
  10. data/README.md +95 -50
  11. data/Rakefile +1 -0
  12. data/doc_config/yard/templates/default/layout/html/setup.rb +1 -1
  13. data/gemfiles/4.0.0.gemfile +10 -7
  14. data/gemfiles/4.0.0.gemfile.lock +103 -79
  15. data/gemfiles/4.0.1.gemfile +10 -7
  16. data/gemfiles/4.0.1.gemfile.lock +109 -83
  17. data/gemfiles/4.1.gemfile +10 -7
  18. data/gemfiles/4.1.gemfile.lock +109 -85
  19. data/gemfiles/4.2.gemfile +10 -9
  20. data/gemfiles/4.2.gemfile.lock +86 -78
  21. data/lib/shoulda/matchers.rb +13 -18
  22. data/lib/shoulda/matchers/action_controller.rb +4 -1
  23. data/lib/shoulda/matchers/action_controller/flash_store.rb +95 -0
  24. data/lib/shoulda/matchers/action_controller/{strong_parameters_matcher.rb → permit_matcher.rb} +147 -30
  25. data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +1 -1
  26. data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +1 -1
  27. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +1 -1
  28. data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +1 -1
  29. data/lib/shoulda/matchers/action_controller/route_matcher.rb +5 -1
  30. data/lib/shoulda/matchers/action_controller/route_params.rb +15 -6
  31. data/lib/shoulda/matchers/action_controller/session_store.rb +34 -0
  32. data/lib/shoulda/matchers/action_controller/set_flash_matcher.rb +30 -136
  33. data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +28 -109
  34. data/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +103 -0
  35. data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +1 -12
  36. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +79 -10
  37. data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +10 -0
  38. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +21 -0
  39. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +24 -0
  40. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +22 -5
  41. data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +29 -10
  42. data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +27 -10
  43. data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +27 -12
  44. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +56 -20
  45. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +3 -11
  46. data/lib/shoulda/matchers/active_model/validation_message_finder.rb +65 -0
  47. data/lib/shoulda/matchers/active_record/association_matcher.rb +40 -6
  48. data/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +21 -7
  49. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +11 -40
  50. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +1 -1
  51. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +2 -6
  52. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +137 -22
  53. data/lib/shoulda/matchers/configuration.rb +20 -0
  54. data/lib/shoulda/matchers/doublespeak.rb +11 -1
  55. data/lib/shoulda/matchers/doublespeak/double.rb +29 -11
  56. data/lib/shoulda/matchers/doublespeak/double_collection.rb +4 -3
  57. data/lib/shoulda/matchers/doublespeak/method_call.rb +35 -0
  58. data/lib/shoulda/matchers/doublespeak/object_double.rb +7 -2
  59. data/lib/shoulda/matchers/doublespeak/proxy_implementation.rb +4 -3
  60. data/lib/shoulda/matchers/doublespeak/stub_implementation.rb +3 -3
  61. data/lib/shoulda/matchers/doublespeak/world.rb +21 -1
  62. data/lib/shoulda/matchers/integrations.rb +43 -0
  63. data/lib/shoulda/matchers/integrations/configuration.rb +68 -0
  64. data/lib/shoulda/matchers/integrations/configuration_error.rb +9 -0
  65. data/lib/shoulda/matchers/integrations/inclusion.rb +20 -0
  66. data/lib/shoulda/matchers/integrations/libraries.rb +15 -0
  67. data/lib/shoulda/matchers/integrations/libraries/action_controller.rb +31 -0
  68. data/lib/shoulda/matchers/integrations/libraries/active_model.rb +26 -0
  69. data/lib/shoulda/matchers/integrations/libraries/active_record.rb +26 -0
  70. data/lib/shoulda/matchers/integrations/libraries/missing_library.rb +19 -0
  71. data/lib/shoulda/matchers/integrations/libraries/rails.rb +30 -0
  72. data/lib/shoulda/matchers/integrations/rails.rb +12 -0
  73. data/lib/shoulda/matchers/integrations/registry.rb +28 -0
  74. data/lib/shoulda/matchers/integrations/test_frameworks.rb +16 -0
  75. data/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +37 -0
  76. data/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +36 -0
  77. data/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +37 -0
  78. data/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +40 -0
  79. data/lib/shoulda/matchers/integrations/test_frameworks/rspec.rb +29 -0
  80. data/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +36 -0
  81. data/lib/shoulda/matchers/rails_shim.rb +0 -40
  82. data/lib/shoulda/matchers/version.rb +1 -1
  83. data/script/SUPPORTED_VERSIONS +1 -1
  84. data/script/update_gems_in_all_appraisals +14 -0
  85. data/shoulda-matchers.gemspec +2 -2
  86. data/spec/acceptance/active_model_integration_spec.rb +4 -1
  87. data/spec/acceptance/independent_matchers_spec.rb +6 -6
  88. data/spec/acceptance/multiple_libraries_integration_spec.rb +52 -0
  89. data/spec/acceptance/rails_integration_spec.rb +15 -5
  90. data/spec/acceptance_spec_helper.rb +8 -0
  91. data/spec/doublespeak_spec_helper.rb +14 -0
  92. data/spec/support/acceptance/adds_shoulda_matchers_to_project.rb +110 -0
  93. data/spec/support/acceptance/helpers.rb +2 -0
  94. data/spec/support/acceptance/helpers/base_helpers.rb +6 -1
  95. data/spec/support/acceptance/helpers/command_helpers.rb +6 -2
  96. data/spec/support/acceptance/helpers/minitest_helpers.rb +0 -8
  97. data/spec/support/acceptance/helpers/n_unit_helpers.rb +25 -0
  98. data/spec/support/acceptance/helpers/rspec_helpers.rb +2 -0
  99. data/spec/support/acceptance/helpers/step_helpers.rb +13 -19
  100. data/spec/support/acceptance/matchers/have_output.rb +1 -1
  101. data/spec/support/tests/bundle.rb +1 -1
  102. data/spec/support/tests/command_runner.rb +25 -13
  103. data/spec/support/tests/current_bundle.rb +47 -0
  104. data/spec/support/tests/database.rb +28 -0
  105. data/spec/support/tests/database_adapters/postgresql.rb +25 -0
  106. data/spec/support/tests/database_adapters/sqlite3.rb +26 -0
  107. data/spec/support/tests/database_configuration.rb +33 -0
  108. data/spec/support/tests/database_configuration_registry.rb +28 -0
  109. data/spec/support/tests/filesystem.rb +25 -2
  110. data/spec/support/unit/helpers/active_record_versions.rb +12 -0
  111. data/spec/support/unit/helpers/class_builder.rb +6 -2
  112. data/spec/support/unit/helpers/column_type_helpers.rb +26 -0
  113. data/spec/support/unit/helpers/controller_builder.rb +0 -28
  114. data/spec/support/unit/helpers/database_helpers.rb +18 -0
  115. data/spec/support/unit/helpers/model_builder.rb +38 -6
  116. data/spec/support/unit/helpers/rails_versions.rb +2 -2
  117. data/spec/support/unit/matchers/fail_with_message_including_matcher.rb +9 -8
  118. data/spec/support/unit/matchers/fail_with_message_matcher.rb +1 -1
  119. data/spec/support/unit/rails_application.rb +29 -13
  120. data/spec/support/unit/record_validating_confirmation_builder.rb +1 -2
  121. data/spec/support/unit/shared_examples/set_session_or_flash.rb +355 -0
  122. data/spec/unit/shoulda/matchers/action_controller/permit_matcher_spec.rb +433 -0
  123. data/spec/unit/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +1 -5
  124. data/spec/unit/shoulda/matchers/action_controller/route_matcher_spec.rb +37 -0
  125. data/spec/unit/shoulda/matchers/action_controller/set_flash_matcher_spec.rb +23 -147
  126. data/spec/unit/shoulda/matchers/action_controller/set_session_matcher_spec.rb +8 -285
  127. data/spec/unit/shoulda/matchers/action_controller/set_session_or_flash_matcher_spec.rb +562 -0
  128. data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +81 -14
  129. data/spec/unit/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +16 -8
  130. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +101 -9
  131. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/even_number_matcher_spec.rb +39 -1
  132. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher_spec.rb +39 -1
  133. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher_spec.rb +39 -0
  134. data/spec/unit/shoulda/matchers/active_model/validate_exclusion_of_matcher_spec.rb +0 -17
  135. data/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb +0 -17
  136. data/spec/unit/shoulda/matchers/active_model/validate_length_of_matcher_spec.rb +0 -17
  137. data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +838 -271
  138. data/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +0 -19
  139. data/spec/unit/shoulda/matchers/active_record/association_matcher_spec.rb +93 -0
  140. data/spec/unit/shoulda/matchers/active_record/association_matchers/model_reflection_spec.rb +3 -3
  141. data/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb +25 -0
  142. data/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb +905 -0
  143. data/spec/unit/shoulda/matchers/doublespeak/double_collection_spec.rb +17 -11
  144. data/spec/unit/shoulda/matchers/doublespeak/double_implementation_registry_spec.rb +1 -1
  145. data/spec/unit/shoulda/matchers/doublespeak/double_spec.rb +144 -43
  146. data/spec/unit/shoulda/matchers/doublespeak/object_double_spec.rb +1 -1
  147. data/spec/unit/shoulda/matchers/doublespeak/proxy_implementation_spec.rb +36 -11
  148. data/spec/unit/shoulda/matchers/doublespeak/stub_implementation_spec.rb +29 -16
  149. data/spec/unit/shoulda/matchers/doublespeak/world_spec.rb +8 -5
  150. data/spec/unit/shoulda/matchers/doublespeak_spec.rb +1 -1
  151. data/spec/unit_spec_helper.rb +15 -14
  152. data/spec/warnings_spy.rb +1 -1
  153. metadata +68 -29
  154. data/docs.watchr +0 -5
  155. data/gemfiles/3.0.gemfile +0 -26
  156. data/gemfiles/3.0.gemfile.lock +0 -173
  157. data/gemfiles/3.1.gemfile +0 -32
  158. data/gemfiles/3.1.gemfile.lock +0 -212
  159. data/gemfiles/3.1_1.9.2.gemfile +0 -32
  160. data/gemfiles/3.1_1.9.2.gemfile.lock +0 -212
  161. data/gemfiles/3.2.gemfile +0 -33
  162. data/gemfiles/3.2.gemfile.lock +0 -212
  163. data/gemfiles/3.2_1.9.2.gemfile +0 -31
  164. data/gemfiles/3.2_1.9.2.gemfile.lock +0 -207
  165. data/lib/shoulda/matchers/assertion_error.rb +0 -27
  166. data/lib/shoulda/matchers/doublespeak/structs.rb +0 -10
  167. data/lib/shoulda/matchers/integrations/nunit_test_case_detection.rb +0 -39
  168. data/lib/shoulda/matchers/integrations/rspec.rb +0 -19
  169. data/lib/shoulda/matchers/integrations/test_unit.rb +0 -34
  170. data/spec/unit/shoulda/matchers/action_controller/strong_parameters_matcher_spec.rb +0 -331
  171. data/spec/unit/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb +0 -564
@@ -1,564 +0,0 @@
1
- require 'unit_spec_helper'
2
-
3
- describe Shoulda::Matchers::ActiveRecord::ValidateUniquenessOfMatcher, type: :model do
4
- context 'a model without a a uniqueness validation' do
5
- it 'rejects' do
6
- model = define_model(:example, attr: :string) { attr_accessible :attr } .new
7
- Example.create!(attr: 'value')
8
- expect(model).not_to matcher
9
- end
10
- end
11
-
12
- context 'a model with a uniqueness validation' do
13
- context 'where the subject has a character limit' do
14
- it 'tests with values within the character limit' do
15
- model = define_model(:example, attr: { type: :string, options: { limit: 1 } }) do
16
- attr_accessible :attr
17
- validates_uniqueness_of :attr
18
- end.new
19
- expect(model).to matcher
20
- end
21
- end
22
-
23
- context 'with an existing record' do
24
- it 'requires a unique value for that attribute' do
25
- create_existing
26
- expect(validating_uniqueness_with_other).to matcher
27
- end
28
-
29
- it 'accepts when the subject is an existing record' do
30
- expect(create_existing).to matcher
31
- end
32
-
33
- it 'rejects when a scope is specified' do
34
- create_existing
35
- expect(validating_uniqueness_with_other).not_to matcher.scoped_to(:other)
36
- end
37
-
38
- def create_existing
39
- define_model_with_other
40
- Example.create!(attr: 'value', other: 1)
41
- end
42
- end
43
-
44
- context 'without an existing record' do
45
- it 'does not require a created instance' do
46
- define_model_with_other
47
- expect(Example.count).to eq 0
48
- expect(validating_uniqueness_with_other).to matcher
49
- end
50
-
51
- context "and the table uses non-nullable columns, set beforehand" do
52
- it "does not require the record to be persisted" do
53
- model = define_model_with_non_nullable_column
54
- record = model.new(required_attribute_name => "some value")
55
- expect(record).to validate_uniqueness_of(unique_attribute_name)
56
- end
57
-
58
- def define_model_with_non_nullable_column
59
- model = define_model(:example,
60
- unique_attribute_name => :string,
61
- required_attribute_name => {
62
- type: :string,
63
- options: { null: false }
64
- }
65
- )
66
-
67
- model.tap do
68
- model.attr_accessible(
69
- required_attribute_name,
70
- unique_attribute_name
71
- )
72
- model.validates_presence_of(required_attribute_name)
73
- model.validates_uniqueness_of(unique_attribute_name)
74
- end
75
- end
76
-
77
- def required_attribute_name
78
- :required_attribute_name
79
- end
80
-
81
- def unique_attribute_name
82
- :unique_attribute_name
83
- end
84
- end
85
- end
86
-
87
- def define_model_with_other(options = {})
88
- @model ||= define_model(:example, attr: :string, other: :integer) do
89
- attr_accessible :attr, :other
90
- validates_uniqueness_of :attr, options
91
- end
92
- end
93
-
94
- def validating_uniqueness_with_other(options = {})
95
- define_model_with_other.new
96
- end
97
- end
98
-
99
- context 'a model with a uniqueness validation, a custom error, and an existing record' do
100
- it 'rejects when the actual message does not match the default message' do
101
- expect(validating_uniqueness_with_existing_record(message: 'Bad value')).
102
- not_to matcher
103
- end
104
-
105
- it 'rejects when the messages do not match' do
106
- expect(validating_uniqueness_with_existing_record(message: 'Bad value')).
107
- not_to matcher.with_message(/abc/)
108
- end
109
-
110
- it 'accepts when the messages match' do
111
- expect(validating_uniqueness_with_existing_record(message: 'Bad value')).
112
- to matcher.with_message(/Bad/)
113
- end
114
-
115
- def validating_uniqueness_with_existing_record(options = {})
116
- model = define_model(:example, attr: :string) do
117
- attr_accessible :attr
118
- validates_uniqueness_of :attr, options
119
- end.new
120
- Example.create!(attr: 'value')
121
- model
122
- end
123
- end
124
-
125
- context 'a model with a scoped uniqueness validation with an existing value' do
126
- it 'accepts when the correct scope is specified' do
127
- expect(validating_scoped_uniqueness([:scope1, :scope2])).
128
- to matcher.scoped_to(:scope1, :scope2)
129
- end
130
-
131
- it 'accepts when the subject is an existing record' do
132
- define_scoped_model([:scope1, :scope2])
133
- expect(create_existing_record).to matcher.scoped_to(:scope1, :scope2)
134
- end
135
-
136
- it 'rejects when too narrow of a scope is specified' do
137
- expect(validating_scoped_uniqueness([:scope1, :scope2])).
138
- not_to matcher.scoped_to(:scope1, :scope2, :other)
139
- end
140
-
141
- it 'rejects when too broad of a scope is specified' do
142
- expect(validating_scoped_uniqueness([:scope1, :scope2])).
143
- not_to matcher.scoped_to(:scope1)
144
- end
145
-
146
- it 'rejects when a different scope is specified' do
147
- expect(validating_scoped_uniqueness([:scope1])).
148
- not_to matcher.scoped_to(:other)
149
- end
150
-
151
- it 'rejects when no scope is specified' do
152
- expect(validating_scoped_uniqueness([:scope1])).not_to matcher
153
- end
154
-
155
- it 'rejects when a non-existent attribute is specified as a scope' do
156
- expect(validating_scoped_uniqueness([:scope1])).
157
- not_to matcher.scoped_to(:fake)
158
- end
159
-
160
- if rails_gte_4_1?
161
- context 'when the scoped attribute is an enum' do
162
- it 'accepts' do
163
- expect(validating_scoped_uniqueness_with_enum([:scope1], scope1: 0)).
164
- to matcher.scoped_to(:scope1)
165
- end
166
-
167
- context 'with a nil value' do
168
- it 'accepts' do
169
- expect(validating_scoped_uniqueness_with_enum([:scope1], scope1: nil)).
170
- to matcher.scoped_to(:scope1)
171
- end
172
- end
173
-
174
- context 'when too narrow of a scope is specified' do
175
- it 'rejects' do
176
- expect(validating_scoped_uniqueness_with_enum_with_two_scopes).
177
- not_to matcher.scoped_to(:scope1, :scope2, :other)
178
- end
179
- end
180
-
181
- context 'when too broad of a scope is specified' do
182
- it 'rejects' do
183
- expect(validating_scoped_uniqueness_with_enum_with_two_scopes).
184
- not_to matcher.scoped_to(:scope1)
185
- end
186
- end
187
-
188
- def validating_scoped_uniqueness_with_enum_with_two_scopes
189
- validating_scoped_uniqueness_with_enum([:scope1, :scope2], scope1: 0, scope2: 0)
190
- end
191
- end
192
- end
193
-
194
- context 'when the scoped attribute is a date' do
195
- it "accepts" do
196
- expect(validating_scoped_uniqueness([:scope1], :date, scope1: Date.today)).
197
- to matcher.scoped_to(:scope1)
198
- end
199
-
200
- context 'with an existing record that conflicts with scope.next' do
201
- it 'accepts' do
202
- expect(validating_scoped_uniqueness_with_conflicting_next(:scope1, :date, scope1: Date.today)).
203
- to matcher.scoped_to(:scope1)
204
- end
205
- end
206
-
207
- context 'when too narrow of a scope is specified' do
208
- it 'rejects' do
209
- expect(validating_scoped_uniqueness([:scope1, :scope2], :date, scope1: Date.today, scope2: Date.today)).
210
- not_to matcher.scoped_to(:scope1, :scope2, :other)
211
- end
212
- end
213
-
214
- context 'when too broad of a scope is specified' do
215
- it 'rejects' do
216
- expect(validating_scoped_uniqueness([:scope1, :scope2], :date, scope1: Date.today, scope2: Date.today)).
217
- not_to matcher.scoped_to(:scope1)
218
- end
219
- end
220
- end
221
-
222
- context 'when the scoped attribute is a datetime' do
223
- it 'accepts' do
224
- expect(validating_scoped_uniqueness([:scope1], :datetime, scope1: DateTime.now)).
225
- to matcher.scoped_to(:scope1)
226
- end
227
-
228
- context 'with an existing record that conflicts with scope.next' do
229
- it 'accepts' do
230
- expect(validating_scoped_uniqueness_with_conflicting_next(:scope1, :datetime, scope1: DateTime.now)).
231
- to matcher.scoped_to(:scope1)
232
- end
233
- end
234
-
235
- context 'with a nil value' do
236
- it 'accepts' do
237
- expect(validating_scoped_uniqueness([:scope1], :datetime, scope1: nil)).
238
- to matcher.scoped_to(:scope1)
239
- end
240
- end
241
-
242
- context 'when too narrow of a scope is specified' do
243
- it 'rejects' do
244
- expect(validating_scoped_uniqueness([:scope1, :scope2], :datetime, scope1: DateTime.now, scope2: DateTime.now)).
245
- not_to matcher.scoped_to(:scope1, :scope2, :other)
246
- end
247
- end
248
-
249
- context 'when too broad of a scope is specified' do
250
- it 'rejects' do
251
- expect(validating_scoped_uniqueness([:scope1, :scope2], :datetime, scope1: DateTime.now, scope2: DateTime.now)).
252
- not_to matcher.scoped_to(:scope1)
253
- end
254
- end
255
- end
256
-
257
- context 'when the scoped attribute is a uuid' do
258
- it 'accepts' do
259
- expect(validating_scoped_uniqueness([:scope1], :uuid, scope1: SecureRandom.uuid)).
260
- to matcher.scoped_to(:scope1)
261
- end
262
-
263
- context 'with an existing record that conflicts with scope.next' do
264
- it 'accepts' do
265
- expect(validating_scoped_uniqueness_with_conflicting_next(:scope1, :uuid, scope1: SecureRandom.uuid)).
266
- to matcher.scoped_to(:scope1)
267
- end
268
- end
269
-
270
- context 'with a nil value' do
271
- it 'accepts' do
272
- expect(validating_scoped_uniqueness([:scope1], :uuid, scope1: nil)).
273
- to matcher.scoped_to(:scope1)
274
- end
275
- end
276
-
277
- context 'when too narrow of a scope is specified' do
278
- it 'rejects' do
279
- record = validating_scoped_uniqueness([:scope1, :scope2], :uuid,
280
- scope1: SecureRandom.uuid,
281
- scope2: SecureRandom.uuid
282
- )
283
- expect(record).not_to matcher.scoped_to(:scope1, :scope2, :other)
284
- end
285
- end
286
-
287
- context 'when too broad of a scope is specified' do
288
- it 'rejects' do
289
- record = validating_scoped_uniqueness([:scope1, :scope2], :uuid,
290
- scope1: SecureRandom.uuid,
291
- scope2: SecureRandom.uuid
292
- )
293
- expect(record).not_to matcher.scoped_to(:scope1)
294
- end
295
- end
296
- end
297
-
298
- def create_existing_record(attributes = {})
299
- @existing ||= create_record(attributes)
300
- end
301
-
302
- def create_record(attributes = {})
303
- default_attributes = {attr: 'value', scope1: 1, scope2: 2, other: 3}
304
- Example.create!(default_attributes.merge(attributes))
305
- end
306
-
307
- def define_scoped_model(scope, scope_attr_type = :integer)
308
- define_model(:example, attr: :string, scope1: scope_attr_type,
309
- scope2: scope_attr_type, other: :integer) do
310
- attr_accessible :attr, :scope1, :scope2, :other
311
- validates_uniqueness_of :attr, scope: scope
312
- end
313
- end
314
-
315
- def validating_scoped_uniqueness(*args)
316
- attributes = args.extract_options!
317
- model = define_scoped_model(*args).new
318
- create_existing_record(attributes)
319
- model
320
- end
321
-
322
- def validating_scoped_uniqueness_with_enum(*args)
323
- attributes = args.extract_options!
324
- model = define_scoped_model(*args)
325
- model.enum scope1: [:foo, :bar]
326
- create_existing_record(attributes)
327
- model.new
328
- end
329
-
330
- def validating_scoped_uniqueness_with_conflicting_next(*args)
331
- attributes = args.extract_options!
332
- model = define_scoped_model(*args).new
333
- 2.times do
334
- attributes[:scope1] = attributes[:scope1].next
335
- create_record(attributes)
336
- end
337
- model
338
- end
339
- end
340
-
341
- context 'a model with a case-sensitive uniqueness validation on a string attribute and an existing record' do
342
- it 'accepts a case-sensitive value for that attribute' do
343
- expect(case_sensitive_validation_with_existing_value(:string)).
344
- to matcher
345
- end
346
-
347
- it 'rejects a case-insensitive value for that attribute' do
348
- expect(case_sensitive_validation_with_existing_value(:string)).
349
- not_to matcher.case_insensitive
350
- end
351
- end
352
-
353
- context 'a model with a case-sensitive uniqueness validation on an integer attribute with an existing value' do
354
- it 'accepts a case-insensitive value for that attribute' do
355
- expect(case_sensitive_validation_with_existing_value(:integer)).
356
- to matcher.case_insensitive
357
- end
358
-
359
- it 'accepts a case-sensitive value for that attribute' do
360
- expect(case_sensitive_validation_with_existing_value(:integer)).to matcher
361
- end
362
- end
363
-
364
- context "when the validation allows nil" do
365
- context "when there is an existing entry with a nil" do
366
- it "should allow_nil" do
367
- model = define_model_with_allow_nil
368
- Example.create!(attr: nil)
369
- expect(model).to matcher.allow_nil
370
- end
371
- end
372
-
373
- if active_model_3_1?
374
- context 'when the subject has a secure password' do
375
- it 'allows nil on the attribute' do
376
- model = define_model(:example, attr: :string, password_digest: :string) do |m|
377
- validates_uniqueness_of :attr, allow_nil: true
378
- has_secure_password
379
- end.new
380
- expect(model).to matcher.allow_nil
381
- end
382
- end
383
- end
384
-
385
- it "should create a nil and verify that it is allowed" do
386
- model = define_model_with_allow_nil
387
- expect(model).to matcher.allow_nil
388
- Example.all.any?{ |instance| instance.attr.nil? }
389
- end
390
-
391
- def define_model_with_allow_nil
392
- define_model(:example, attr: :integer) do
393
- attr_accessible :attr
394
- validates_uniqueness_of :attr, allow_nil: true
395
- end.new
396
- end
397
- end
398
-
399
- context "when the validation does not allow a nil" do
400
- context "when there is an existing entry with a nil" do
401
- it "should not allow_nil" do
402
- model = define_model_without_allow_nil
403
- Example.create!(attr: nil)
404
- expect(model).not_to matcher.allow_nil
405
- end
406
- end
407
-
408
- it "should not allow_nil" do
409
- model = define_model_without_allow_nil
410
- expect(model).not_to matcher.allow_nil
411
- end
412
-
413
- def define_model_without_allow_nil
414
- define_model(:example, attr: :integer) do
415
- attr_accessible :attr
416
- validates_uniqueness_of :attr
417
- end.new
418
- end
419
- end
420
-
421
- context 'when the validation allows blank' do
422
- context 'when there is an existing record with a blank value' do
423
- it 'accepts' do
424
- model = model_allowing_blank
425
- model.create!(attribute_name => '')
426
- expect(model.new).to matcher.allow_blank
427
- end
428
- end
429
-
430
- context 'when there is not an an existing record with a blank value' do
431
- it 'still accepts' do
432
- expect(record_allowing_blank).to matcher.allow_blank
433
- end
434
-
435
- it 'automatically creates a record' do
436
- model = model_allowing_blank
437
- matcher.allow_blank.matches?(model.new)
438
-
439
- record_created = model.all.any? do |instance|
440
- instance.__send__(attribute_name).blank?
441
- end
442
-
443
- expect(record_created).to be true
444
- end
445
- end
446
-
447
- def attribute_name
448
- :attr
449
- end
450
-
451
- def model_allowing_blank
452
- _attribute_name = attribute_name
453
-
454
- define_model(:example, attribute_name => :string) do
455
- attr_accessible _attribute_name
456
- validates_uniqueness_of _attribute_name, allow_blank: true
457
- end
458
- end
459
-
460
- def record_allowing_blank
461
- model_allowing_blank.new
462
- end
463
- end
464
-
465
- context 'when the validation does not allow blank' do
466
- context 'when there is an existing entry with a blank value' do
467
- it 'rejects' do
468
- model = model_disallowing_blank
469
- model.create!(attribute_name => '')
470
- expect(model.new).not_to matcher.allow_blank
471
- end
472
- end
473
-
474
- it 'should not allow_blank' do
475
- expect(record_disallowing_blank).not_to matcher.allow_blank
476
- end
477
-
478
- def attribute_name
479
- :attr
480
- end
481
-
482
- def model_disallowing_blank
483
- _attribute_name = attribute_name
484
-
485
- define_model(:example, attribute_name => :string) do
486
- attr_accessible _attribute_name
487
- validates_uniqueness_of _attribute_name, allow_blank: false
488
- end
489
- end
490
-
491
- def record_disallowing_blank
492
- model_disallowing_blank.new
493
- end
494
- end
495
-
496
- context "when testing that a polymorphic *_type column is one of the validation scopes" do
497
- it "sets that column to a meaningful value that works with other validations on the same column" do
498
- user_model = define_model :user
499
- favorite_columns = {
500
- favoriteable_id: { type: :integer, options: { null: false } },
501
- favoriteable_type: { type: :string, options: { null: false } }
502
- }
503
- favorite_model = define_model :favorite, favorite_columns do
504
- attr_accessible :favoriteable
505
- belongs_to :favoriteable, polymorphic: true
506
- validates :favoriteable, presence: true
507
- validates :favoriteable_id, uniqueness: { scope: :favoriteable_type }
508
- end
509
-
510
- user = user_model.create!
511
- favorite_model.create!(favoriteable: user)
512
- new_favorite = favorite_model.new
513
-
514
- expect(new_favorite).
515
- to validate_uniqueness_of(:favoriteable_id).
516
- scoped_to(:favoriteable_type)
517
- end
518
-
519
- context "if the model the *_type column refers to is namespaced, and shares the last part of its name with an existing model" do
520
- it "still works" do
521
- define_class 'User'
522
- define_module 'Models'
523
- user_model = define_model 'Models::User'
524
- favorite_columns = {
525
- favoriteable_id: { type: :integer, options: { null: false } },
526
- favoriteable_type: { type: :string, options: { null: false } }
527
- }
528
- favorite_model = define_model 'Models::Favorite', favorite_columns do
529
- attr_accessible :favoriteable
530
- belongs_to :favoriteable, polymorphic: true
531
- validates :favoriteable, presence: true
532
- validates :favoriteable_id, uniqueness: { scope: :favoriteable_type }
533
- end
534
-
535
- user = user_model.create!
536
- favorite_model.create!(favoriteable: user)
537
- new_favorite = favorite_model.new
538
-
539
- expect(new_favorite).
540
- to validate_uniqueness_of(:favoriteable_id).
541
- scoped_to(:favoriteable_type)
542
- end
543
- end
544
- end
545
-
546
- def case_sensitive_validation_with_existing_value(attr_type)
547
- model = define_model(:example, attr: attr_type) do
548
- attr_accessible :attr
549
- validates_uniqueness_of :attr, case_sensitive: true
550
- end.new
551
- if attr_type == :string
552
- Example.create!(attr: 'value')
553
- elsif attr_type == :integer
554
- Example.create!(attr: 1)
555
- else
556
- raise 'Must be :string or :integer'
557
- end
558
- model
559
- end
560
-
561
- def matcher
562
- validate_uniqueness_of(:attr)
563
- end
564
- end