shoulda-matchers 3.1.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. checksums.yaml +5 -5
  2. data/{MIT-LICENSE → LICENSE} +1 -1
  3. data/README.md +407 -232
  4. data/docs/errors/NonCaseSwappableValueError.md +2 -2
  5. data/lib/shoulda/matchers/action_controller/callback_matcher.rb +7 -80
  6. data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +4 -3
  7. data/lib/shoulda/matchers/action_controller/flash_store.rb +2 -4
  8. data/lib/shoulda/matchers/action_controller/permit_matcher.rb +36 -30
  9. data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +8 -10
  10. data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +7 -9
  11. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +18 -15
  12. data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +3 -2
  13. data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +3 -3
  14. data/lib/shoulda/matchers/action_controller/route_matcher.rb +88 -29
  15. data/lib/shoulda/matchers/action_controller/route_params.rb +2 -2
  16. data/lib/shoulda/matchers/action_controller/set_flash_matcher.rb +4 -4
  17. data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +3 -3
  18. data/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +19 -13
  19. data/lib/shoulda/matchers/action_controller.rb +2 -0
  20. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error.rb +1 -1
  21. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +5 -9
  22. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb +2 -2
  23. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters.rb +1 -1
  24. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators.rb +1 -1
  25. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +42 -39
  26. data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +1 -1
  27. data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +52 -26
  28. data/lib/shoulda/matchers/active_model/helpers.rb +2 -2
  29. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +32 -30
  30. data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +2 -1
  31. data/lib/shoulda/matchers/active_model/qualifiers/allow_blank.rb +26 -0
  32. data/lib/shoulda/matchers/active_model/qualifiers/allow_nil.rb +26 -0
  33. data/lib/shoulda/matchers/active_model/qualifiers/ignoring_interference_by_writer.rb +1 -1
  34. data/lib/shoulda/matchers/active_model/qualifiers.rb +2 -0
  35. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +30 -6
  36. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +8 -3
  37. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +31 -16
  38. data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +52 -16
  39. data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +137 -84
  40. data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +159 -46
  41. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +130 -66
  42. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +251 -24
  43. data/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +12 -9
  44. data/lib/shoulda/matchers/active_model/validation_matcher.rb +38 -6
  45. data/lib/shoulda/matchers/active_model/validation_message_finder.rb +2 -4
  46. data/lib/shoulda/matchers/active_model/validator.rb +4 -9
  47. data/lib/shoulda/matchers/active_model.rb +3 -5
  48. data/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +10 -7
  49. data/lib/shoulda/matchers/active_record/association_matcher.rb +386 -111
  50. data/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +5 -2
  51. data/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +4 -4
  52. data/lib/shoulda/matchers/active_record/association_matchers/inverse_of_matcher.rb +1 -1
  53. data/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +11 -6
  54. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +14 -15
  55. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +30 -8
  56. data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +34 -11
  57. data/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb +69 -0
  58. data/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +1 -1
  59. data/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +74 -0
  60. data/lib/shoulda/matchers/active_record/association_matchers/source_matcher.rb +3 -2
  61. data/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +7 -5
  62. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +458 -42
  63. data/lib/shoulda/matchers/active_record/have_attached_matcher.rb +185 -0
  64. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +63 -23
  65. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +164 -48
  66. data/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +106 -0
  67. data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +13 -11
  68. data/lib/shoulda/matchers/active_record/have_rich_text_matcher.rb +83 -0
  69. data/lib/shoulda/matchers/active_record/have_secure_token_matcher.rb +132 -0
  70. data/lib/shoulda/matchers/active_record/serialize_matcher.rb +18 -18
  71. data/lib/shoulda/matchers/active_record/uniqueness/test_model_creator.rb +1 -3
  72. data/lib/shoulda/matchers/active_record/uniqueness/test_models.rb +0 -2
  73. data/lib/shoulda/matchers/active_record/uniqueness.rb +1 -1
  74. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +430 -200
  75. data/lib/shoulda/matchers/active_record.rb +28 -20
  76. data/lib/shoulda/matchers/configuration.rb +12 -1
  77. data/lib/shoulda/matchers/doublespeak/double.rb +1 -1
  78. data/lib/shoulda/matchers/doublespeak/double_collection.rb +3 -3
  79. data/lib/shoulda/matchers/doublespeak/double_implementation_registry.rb +8 -5
  80. data/lib/shoulda/matchers/doublespeak/object_double.rb +6 -2
  81. data/lib/shoulda/matchers/doublespeak/stub_implementation.rb +1 -5
  82. data/lib/shoulda/matchers/doublespeak/world.rb +2 -2
  83. data/lib/shoulda/matchers/doublespeak.rb +2 -1
  84. data/lib/shoulda/matchers/error.rb +1 -1
  85. data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +109 -29
  86. data/lib/shoulda/matchers/independent.rb +2 -2
  87. data/lib/shoulda/matchers/integrations/configuration.rb +8 -4
  88. data/lib/shoulda/matchers/integrations/libraries/action_controller.rb +1 -1
  89. data/lib/shoulda/matchers/integrations/libraries/rails.rb +2 -2
  90. data/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +1 -1
  91. data/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +1 -1
  92. data/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +1 -1
  93. data/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +1 -1
  94. data/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +1 -1
  95. data/lib/shoulda/matchers/rails_shim.rb +172 -51
  96. data/lib/shoulda/matchers/routing.rb +2 -2
  97. data/lib/shoulda/matchers/util/word_wrap.rb +17 -12
  98. data/lib/shoulda/matchers/util.rb +39 -5
  99. data/lib/shoulda/matchers/version.rb +1 -1
  100. data/lib/shoulda/matchers/warn.rb +4 -3
  101. data/shoulda-matchers.gemspec +33 -15
  102. metadata +31 -338
  103. data/.gitignore +0 -12
  104. data/.hound.yml +0 -3
  105. data/.hound_config/ruby.yml +0 -12
  106. data/.travis.yml +0 -19
  107. data/.yardopts +0 -10
  108. data/Appraisals +0 -73
  109. data/CONTRIBUTING.md +0 -101
  110. data/Gemfile +0 -15
  111. data/Gemfile.lock +0 -70
  112. data/NEWS.md +0 -986
  113. data/Rakefile +0 -39
  114. data/custom_plan.rb +0 -88
  115. data/doc_config/gh-pages/index.html.erb +0 -9
  116. data/doc_config/yard/setup.rb +0 -22
  117. data/doc_config/yard/templates/default/fulldoc/html/css/bootstrap.css +0 -5967
  118. data/doc_config/yard/templates/default/fulldoc/html/css/full_list.css +0 -12
  119. data/doc_config/yard/templates/default/fulldoc/html/css/global.css +0 -62
  120. data/doc_config/yard/templates/default/fulldoc/html/css/solarized.css +0 -69
  121. data/doc_config/yard/templates/default/fulldoc/html/css/style.css +0 -312
  122. data/doc_config/yard/templates/default/fulldoc/html/full_list.erb +0 -32
  123. data/doc_config/yard/templates/default/fulldoc/html/full_list_class.erb +0 -1
  124. data/doc_config/yard/templates/default/fulldoc/html/full_list_method.erb +0 -8
  125. data/doc_config/yard/templates/default/fulldoc/html/js/app.js +0 -298
  126. data/doc_config/yard/templates/default/fulldoc/html/js/full_list.js +0 -1
  127. data/doc_config/yard/templates/default/fulldoc/html/js/jquery.stickyheaders.js +0 -289
  128. data/doc_config/yard/templates/default/fulldoc/html/js/underscore.min.js +0 -6
  129. data/doc_config/yard/templates/default/fulldoc/html/setup.rb +0 -8
  130. data/doc_config/yard/templates/default/layout/html/breadcrumb.erb +0 -14
  131. data/doc_config/yard/templates/default/layout/html/fonts.erb +0 -1
  132. data/doc_config/yard/templates/default/layout/html/footer.erb +0 -6
  133. data/doc_config/yard/templates/default/layout/html/layout.erb +0 -23
  134. data/doc_config/yard/templates/default/layout/html/search.erb +0 -13
  135. data/doc_config/yard/templates/default/layout/html/setup.rb +0 -40
  136. data/doc_config/yard/templates/default/method_details/html/source.erb +0 -10
  137. data/doc_config/yard/templates/default/module/html/box_info.erb +0 -31
  138. data/gemfiles/4.0.0.gemfile +0 -38
  139. data/gemfiles/4.0.0.gemfile.lock +0 -223
  140. data/gemfiles/4.0.1.gemfile +0 -38
  141. data/gemfiles/4.0.1.gemfile.lock +0 -225
  142. data/gemfiles/4.1.gemfile +0 -38
  143. data/gemfiles/4.1.gemfile.lock +0 -220
  144. data/gemfiles/4.2.gemfile +0 -38
  145. data/gemfiles/4.2.gemfile.lock +0 -243
  146. data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +0 -159
  147. data/lib/shoulda/matchers/independent/delegate_method_matcher/stubbed_target.rb +0 -37
  148. data/script/SUPPORTED_VERSIONS +0 -1
  149. data/script/install_gems_in_all_appraisals +0 -14
  150. data/script/run_all_tests +0 -14
  151. data/script/update_gem_in_all_appraisals +0 -15
  152. data/script/update_gems_in_all_appraisals +0 -14
  153. data/spec/acceptance/active_model_integration_spec.rb +0 -23
  154. data/spec/acceptance/independent_matchers_spec.rb +0 -125
  155. data/spec/acceptance/multiple_libraries_integration_spec.rb +0 -55
  156. data/spec/acceptance/rails_integration_spec.rb +0 -156
  157. data/spec/acceptance_spec_helper.rb +0 -23
  158. data/spec/doublespeak_spec_helper.rb +0 -2
  159. data/spec/report_warnings.rb +0 -7
  160. data/spec/spec_helper.rb +0 -21
  161. data/spec/support/acceptance/adds_shoulda_matchers_to_project.rb +0 -133
  162. data/spec/support/acceptance/helpers/active_model_helpers.rb +0 -11
  163. data/spec/support/acceptance/helpers/array_helpers.rb +0 -13
  164. data/spec/support/acceptance/helpers/base_helpers.rb +0 -19
  165. data/spec/support/acceptance/helpers/command_helpers.rb +0 -55
  166. data/spec/support/acceptance/helpers/file_helpers.rb +0 -19
  167. data/spec/support/acceptance/helpers/gem_helpers.rb +0 -31
  168. data/spec/support/acceptance/helpers/minitest_helpers.rb +0 -11
  169. data/spec/support/acceptance/helpers/n_unit_helpers.rb +0 -25
  170. data/spec/support/acceptance/helpers/pluralization_helpers.rb +0 -13
  171. data/spec/support/acceptance/helpers/rails_version_helpers.rb +0 -11
  172. data/spec/support/acceptance/helpers/rspec_helpers.rb +0 -24
  173. data/spec/support/acceptance/helpers/ruby_version_helpers.rb +0 -9
  174. data/spec/support/acceptance/helpers/step_helpers.rb +0 -127
  175. data/spec/support/acceptance/helpers.rb +0 -31
  176. data/spec/support/acceptance/matchers/have_output.rb +0 -31
  177. data/spec/support/acceptance/matchers/indicate_number_of_tests_was_run_matcher.rb +0 -55
  178. data/spec/support/acceptance/matchers/indicate_that_tests_were_run_matcher.rb +0 -103
  179. data/spec/support/tests/bundle.rb +0 -94
  180. data/spec/support/tests/command_runner.rb +0 -230
  181. data/spec/support/tests/current_bundle.rb +0 -61
  182. data/spec/support/tests/database.rb +0 -28
  183. data/spec/support/tests/database_adapters/postgresql.rb +0 -25
  184. data/spec/support/tests/database_adapters/sqlite3.rb +0 -26
  185. data/spec/support/tests/database_configuration.rb +0 -33
  186. data/spec/support/tests/database_configuration_registry.rb +0 -28
  187. data/spec/support/tests/filesystem.rb +0 -100
  188. data/spec/support/tests/version.rb +0 -45
  189. data/spec/support/unit/active_record/create_table.rb +0 -54
  190. data/spec/support/unit/attribute.rb +0 -47
  191. data/spec/support/unit/capture.rb +0 -40
  192. data/spec/support/unit/change_value.rb +0 -111
  193. data/spec/support/unit/create_model_arguments/basic.rb +0 -135
  194. data/spec/support/unit/create_model_arguments/has_many.rb +0 -15
  195. data/spec/support/unit/create_model_arguments/uniqueness_matcher.rb +0 -74
  196. data/spec/support/unit/helpers/active_model_helpers.rb +0 -27
  197. data/spec/support/unit/helpers/active_model_versions.rb +0 -28
  198. data/spec/support/unit/helpers/active_record_versions.rb +0 -24
  199. data/spec/support/unit/helpers/active_resource_builder.rb +0 -27
  200. data/spec/support/unit/helpers/allow_value_matcher_helpers.rb +0 -15
  201. data/spec/support/unit/helpers/class_builder.rb +0 -90
  202. data/spec/support/unit/helpers/column_type_helpers.rb +0 -26
  203. data/spec/support/unit/helpers/confirmation_matcher_helpers.rb +0 -17
  204. data/spec/support/unit/helpers/controller_builder.rb +0 -63
  205. data/spec/support/unit/helpers/database_helpers.rb +0 -20
  206. data/spec/support/unit/helpers/i18n_faker.rb +0 -15
  207. data/spec/support/unit/helpers/mailer_builder.rb +0 -12
  208. data/spec/support/unit/helpers/model_builder.rb +0 -114
  209. data/spec/support/unit/helpers/rails_versions.rb +0 -28
  210. data/spec/support/unit/helpers/validation_matcher_scenario_helpers.rb +0 -44
  211. data/spec/support/unit/i18n.rb +0 -7
  212. data/spec/support/unit/load_environment.rb +0 -12
  213. data/spec/support/unit/matchers/deprecate.rb +0 -60
  214. data/spec/support/unit/matchers/fail_with_message_including_matcher.rb +0 -51
  215. data/spec/support/unit/matchers/fail_with_message_matcher.rb +0 -62
  216. data/spec/support/unit/matchers/print_warning_including.rb +0 -59
  217. data/spec/support/unit/model_creation_strategies/active_model.rb +0 -111
  218. data/spec/support/unit/model_creation_strategies/active_record.rb +0 -77
  219. data/spec/support/unit/model_creators/active_model.rb +0 -39
  220. data/spec/support/unit/model_creators/active_record/has_and_belongs_to_many.rb +0 -95
  221. data/spec/support/unit/model_creators/active_record/has_many.rb +0 -67
  222. data/spec/support/unit/model_creators/active_record/uniqueness_matcher.rb +0 -42
  223. data/spec/support/unit/model_creators/active_record.rb +0 -43
  224. data/spec/support/unit/model_creators/basic.rb +0 -97
  225. data/spec/support/unit/model_creators.rb +0 -19
  226. data/spec/support/unit/rails_application.rb +0 -126
  227. data/spec/support/unit/record_builder_with_i18n_validation_message.rb +0 -69
  228. data/spec/support/unit/record_validating_confirmation_builder.rb +0 -51
  229. data/spec/support/unit/record_with_different_error_attribute_builder.rb +0 -92
  230. data/spec/support/unit/shared_examples/ignoring_interference_by_writer.rb +0 -79
  231. data/spec/support/unit/shared_examples/numerical_submatcher.rb +0 -17
  232. data/spec/support/unit/shared_examples/set_session_or_flash.rb +0 -360
  233. data/spec/support/unit/validation_matcher_scenario.rb +0 -62
  234. data/spec/unit/shoulda/matchers/action_controller/callback_matcher_spec.rb +0 -82
  235. data/spec/unit/shoulda/matchers/action_controller/filter_param_matcher_spec.rb +0 -28
  236. data/spec/unit/shoulda/matchers/action_controller/permit_matcher_spec.rb +0 -592
  237. data/spec/unit/shoulda/matchers/action_controller/redirect_to_matcher_spec.rb +0 -42
  238. data/spec/unit/shoulda/matchers/action_controller/render_template_matcher_spec.rb +0 -76
  239. data/spec/unit/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +0 -62
  240. data/spec/unit/shoulda/matchers/action_controller/rescue_from_matcher_spec.rb +0 -90
  241. data/spec/unit/shoulda/matchers/action_controller/respond_with_matcher_spec.rb +0 -31
  242. data/spec/unit/shoulda/matchers/action_controller/route_matcher_spec.rb +0 -330
  243. data/spec/unit/shoulda/matchers/action_controller/route_params_spec.rb +0 -30
  244. data/spec/unit/shoulda/matchers/action_controller/set_flash_matcher_spec.rb +0 -67
  245. data/spec/unit/shoulda/matchers/action_controller/set_session_matcher_spec.rb +0 -17
  246. data/spec/unit/shoulda/matchers/action_controller/set_session_or_flash_matcher_spec.rb +0 -562
  247. data/spec/unit/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +0 -115
  248. data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +0 -823
  249. data/spec/unit/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +0 -86
  250. data/spec/unit/shoulda/matchers/active_model/have_secure_password_matcher_spec.rb +0 -20
  251. data/spec/unit/shoulda/matchers/active_model/helpers_spec.rb +0 -162
  252. data/spec/unit/shoulda/matchers/active_model/validate_absence_of_matcher_spec.rb +0 -266
  253. data/spec/unit/shoulda/matchers/active_model/validate_acceptance_of_matcher_spec.rb +0 -91
  254. data/spec/unit/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb +0 -149
  255. data/spec/unit/shoulda/matchers/active_model/validate_exclusion_of_matcher_spec.rb +0 -207
  256. data/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb +0 -1015
  257. data/spec/unit/shoulda/matchers/active_model/validate_length_of_matcher_spec.rb +0 -288
  258. data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +0 -1837
  259. data/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +0 -380
  260. data/spec/unit/shoulda/matchers/active_record/accept_nested_attributes_for_matcher_spec.rb +0 -107
  261. data/spec/unit/shoulda/matchers/active_record/association_matcher_spec.rb +0 -1242
  262. data/spec/unit/shoulda/matchers/active_record/association_matchers/model_reflection_spec.rb +0 -251
  263. data/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb +0 -168
  264. data/spec/unit/shoulda/matchers/active_record/have_db_column_matcher_spec.rb +0 -111
  265. data/spec/unit/shoulda/matchers/active_record/have_db_index_matcher_spec.rb +0 -85
  266. data/spec/unit/shoulda/matchers/active_record/have_readonly_attributes_matcher_spec.rb +0 -41
  267. data/spec/unit/shoulda/matchers/active_record/serialize_matcher_spec.rb +0 -86
  268. data/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb +0 -1418
  269. data/spec/unit/shoulda/matchers/doublespeak/double_collection_spec.rb +0 -190
  270. data/spec/unit/shoulda/matchers/doublespeak/double_implementation_registry_spec.rb +0 -21
  271. data/spec/unit/shoulda/matchers/doublespeak/double_spec.rb +0 -271
  272. data/spec/unit/shoulda/matchers/doublespeak/object_double_spec.rb +0 -77
  273. data/spec/unit/shoulda/matchers/doublespeak/proxy_implementation_spec.rb +0 -72
  274. data/spec/unit/shoulda/matchers/doublespeak/stub_implementation_spec.rb +0 -101
  275. data/spec/unit/shoulda/matchers/doublespeak/world_spec.rb +0 -80
  276. data/spec/unit/shoulda/matchers/doublespeak_spec.rb +0 -27
  277. data/spec/unit/shoulda/matchers/independent/delegate_method_matcher/stubbed_target_spec.rb +0 -43
  278. data/spec/unit/shoulda/matchers/independent/delegate_method_matcher_spec.rb +0 -517
  279. data/spec/unit/shoulda/matchers/routing/route_matcher_spec.rb +0 -242
  280. data/spec/unit/shoulda/matchers/util/word_wrap_spec.rb +0 -252
  281. data/spec/unit_spec_helper.rb +0 -46
  282. data/spec/warnings_spy/filesystem.rb +0 -45
  283. data/spec/warnings_spy/partitioner.rb +0 -36
  284. data/spec/warnings_spy/reader.rb +0 -53
  285. data/spec/warnings_spy/reporter.rb +0 -88
  286. data/spec/warnings_spy.rb +0 -64
  287. data/tasks/documentation.rb +0 -199
  288. data/zeus.json +0 -11
@@ -1,1418 +0,0 @@
1
- require 'unit_spec_helper'
2
-
3
- describe Shoulda::Matchers::ActiveRecord::ValidateUniquenessOfMatcher, type: :model do
4
- shared_context 'it supports scoped attributes of a certain type' do |options = {}|
5
- column_type = options.fetch(:column_type)
6
- value_type = options.fetch(:value_type, column_type)
7
- array = options.fetch(:array, false)
8
-
9
- context 'when the correct scope is specified' do
10
- context 'when the subject is a new record' do
11
- it 'accepts' do
12
- record = build_record_validating_uniqueness(
13
- scopes: [
14
- build_attribute(name: :scope1),
15
- { name: :scope2 }
16
- ]
17
- )
18
- expect(record).to validate_uniqueness.scoped_to(:scope1, :scope2)
19
- end
20
-
21
- it 'still accepts if the scope is unset beforehand' do
22
- record = build_record_validating_uniqueness(
23
- scopes: [ build_attribute(name: :scope, value: nil) ]
24
- )
25
-
26
- expect(record).to validate_uniqueness.scoped_to(:scope)
27
- end
28
- end
29
-
30
- context 'when the subject is an existing record' do
31
- it 'accepts' do
32
- record = create_record_validating_uniqueness(
33
- scopes: [
34
- build_attribute(name: :scope1),
35
- { name: :scope2 }
36
- ]
37
- )
38
-
39
- expect(record).to validate_uniqueness.scoped_to(:scope1, :scope2)
40
- end
41
-
42
- it 'still accepts if the scope is unset beforehand' do
43
- record = create_record_validating_uniqueness(
44
- scopes: [ build_attribute(name: :scope, value: nil) ]
45
- )
46
-
47
- expect(record).to validate_uniqueness.scoped_to(:scope)
48
- end
49
- end
50
- end
51
-
52
- context "when more than one record exists that has the next version of the attribute's value" do
53
- it 'accepts' do
54
- value1 = dummy_value_for(value_type, array: array)
55
- value2 = next_version_of(value1, value_type)
56
- value3 = next_version_of(value2, value_type)
57
- model = define_model_validating_uniqueness(
58
- scopes: [ build_attribute(name: :scope) ]
59
- )
60
- create_record_from(model, scope: value2)
61
- create_record_from(model, scope: value3)
62
- record = build_record_from(model, scope: value1)
63
-
64
- expect(record).to validate_uniqueness.scoped_to(:scope)
65
- end
66
- end
67
-
68
- context 'when too narrow of a scope is specified' do
69
- it 'rejects with an appropriate failure message' do
70
- record = build_record_validating_uniqueness(
71
- scopes: [
72
- build_attribute(name: :scope1),
73
- build_attribute(name: :scope2)
74
- ],
75
- additional_attributes: [:other]
76
- )
77
-
78
- assertion = lambda do
79
- expect(record).
80
- to validate_uniqueness.
81
- scoped_to(:scope1, :scope2, :other)
82
- end
83
-
84
- message = <<-MESSAGE
85
- Example did not properly validate that :attr is case-sensitively unique
86
- within the scope of :scope1, :scope2, and :other.
87
- Expected the validation to be scoped to :scope1, :scope2, and :other,
88
- but it was scoped to :scope1 and :scope2 instead.
89
- MESSAGE
90
-
91
- expect(&assertion).to fail_with_message(message)
92
- end
93
- end
94
-
95
- context 'when too broad of a scope is specified' do
96
- it 'rejects with an appropriate failure message' do
97
- record = build_record_validating_uniqueness(
98
- scopes: [
99
- build_attribute(name: :scope1),
100
- build_attribute(name: :scope2)
101
- ],
102
- )
103
-
104
- assertion = lambda do
105
- expect(record).
106
- to validate_uniqueness.
107
- scoped_to(:scope1)
108
- end
109
-
110
- message = <<-MESSAGE
111
- Example did not properly validate that :attr is case-sensitively unique
112
- within the scope of :scope1.
113
- Expected the validation to be scoped to :scope1, but it was scoped to
114
- :scope1 and :scope2 instead.
115
- MESSAGE
116
-
117
- expect(&assertion).to fail_with_message(message)
118
- end
119
- end
120
-
121
- context 'when a different scope is specified' do
122
- it 'rejects with an appropriate failure message' do
123
- record = build_record_validating_uniqueness(
124
- scopes: [ build_attribute(name: :other) ],
125
- additional_attributes: [:scope]
126
- )
127
- assertion = lambda do
128
- expect(record).
129
- to validate_uniqueness.
130
- scoped_to(:scope)
131
- end
132
-
133
- message = <<-MESSAGE
134
- Example did not properly validate that :attr is case-sensitively unique
135
- within the scope of :scope.
136
- Expected the validation to be scoped to :scope, but it was scoped to
137
- :other instead.
138
- MESSAGE
139
-
140
- expect(&assertion).to fail_with_message(message)
141
- end
142
- end
143
-
144
- context 'when no scope is specified' do
145
- it 'rejects with an appropriate failure message' do
146
- record = build_record_validating_uniqueness(
147
- scopes: [ build_attribute(name: :scope) ]
148
- )
149
-
150
- assertion = lambda do
151
- expect(record).to validate_uniqueness
152
- end
153
-
154
- message = <<-MESSAGE
155
- Example did not properly validate that :attr is case-sensitively unique.
156
- Expected the validation not to be scoped to anything, but it was
157
- scoped to :scope instead.
158
- MESSAGE
159
-
160
- expect(&assertion).to fail_with_message(message)
161
- end
162
-
163
- context 'if the scope attribute is unset in the record given to the matcher' do
164
- it 'rejects with an appropriate failure message' do
165
- record = build_record_validating_uniqueness(
166
- scopes: [ build_attribute(name: :scope, value: nil) ]
167
- )
168
-
169
- assertion = lambda do
170
- expect(record).to validate_uniqueness
171
- end
172
-
173
- message = <<-MESSAGE
174
- Example did not properly validate that :attr is case-sensitively unique.
175
- Expected the validation not to be scoped to anything, but it was
176
- scoped to :scope instead.
177
- MESSAGE
178
-
179
- expect(&assertion).to fail_with_message(message)
180
- end
181
- end
182
- end
183
-
184
- context 'when a non-existent attribute is specified as a scope' do
185
- context 'when there is more than one scope' do
186
- it 'rejects with an appropriate failure message (and does not raise an error)' do
187
- record = build_record_validating_uniqueness(
188
- scopes: [ build_attribute(name: :scope) ]
189
- )
190
-
191
- assertion = lambda do
192
- expect(record).to validate_uniqueness.scoped_to(:non_existent)
193
- end
194
-
195
- message = <<-MESSAGE.strip
196
- Example did not properly validate that :attr is case-sensitively unique
197
- within the scope of :non_existent.
198
- :non_existent does not seem to be an attribute on Example.
199
- MESSAGE
200
-
201
- expect(&assertion).to fail_with_message(message)
202
- end
203
- end
204
-
205
- context 'when there is more than one scope' do
206
- it 'rejects with an appropriate failure message (and does not raise an error)' do
207
- record = build_record_validating_uniqueness(
208
- scopes: [ build_attribute(name: :scope) ]
209
- )
210
-
211
- assertion = lambda do
212
- expect(record).to validate_uniqueness.scoped_to(
213
- :non_existent1,
214
- :non_existent2
215
- )
216
- end
217
-
218
- message = <<-MESSAGE.strip
219
- Example did not properly validate that :attr is case-sensitively unique
220
- within the scope of :non_existent1 and :non_existent2.
221
- :non_existent1 and :non_existent2 do not seem to be attributes on
222
- Example.
223
- MESSAGE
224
-
225
- expect(&assertion).to fail_with_message(message)
226
- end
227
- end
228
- end
229
-
230
- define_method(:build_attribute) do |attribute_options|
231
- attribute_options.deep_merge(
232
- column_type: column_type,
233
- value_type: value_type,
234
- options: { array: array }
235
- )
236
- end
237
- end
238
-
239
- context 'when the model does not have a uniqueness validation' do
240
- it 'rejects with an appropriate failure message' do
241
- model = define_model_without_validation
242
- model.create!(attribute_name => 'value')
243
-
244
- assertion = lambda do
245
- expect(model.new).to validate_uniqueness_of(attribute_name)
246
- end
247
-
248
- message = <<-MESSAGE
249
- Example did not properly validate that :attr is case-sensitively unique.
250
- Given an existing Example whose :attr is ‹"value"›, after making a new
251
- Example and setting its :attr to ‹"value"› as well, the matcher
252
- expected the new Example to be invalid, but it was valid instead.
253
- MESSAGE
254
-
255
- expect(&assertion).to fail_with_message(message)
256
- end
257
- end
258
-
259
- context 'when the model has a uniqueness validation' do
260
- context 'when the attribute has a character limit' do
261
- it 'accepts' do
262
- record = build_record_validating_uniqueness(
263
- attribute_type: :string,
264
- attribute_options: { limit: 1 }
265
- )
266
-
267
- expect(record).to validate_uniqueness
268
- end
269
- end
270
-
271
- context 'when the record is created beforehand' do
272
- context 'when the subject is a new record' do
273
- it 'accepts' do
274
- create_record_validating_uniqueness
275
- expect(new_record_validating_uniqueness).
276
- to validate_uniqueness
277
- end
278
- end
279
-
280
- context 'when the subject is an existing record' do
281
- it 'accepts' do
282
- expect(existing_record_validating_uniqueness).to validate_uniqueness
283
- end
284
- end
285
-
286
- context 'when the validation has no scope and a scope is specified' do
287
- it 'rejects with an appropriate failure message' do
288
- model = define_model_validating_uniqueness(
289
- additional_attributes: [:other]
290
- )
291
- create_record_from(model)
292
- record = build_record_from(model)
293
-
294
- assertion = lambda do
295
- expect(record).to validate_uniqueness.scoped_to(:other)
296
- end
297
-
298
- message = <<-MESSAGE
299
- Example did not properly validate that :attr is case-sensitively unique
300
- within the scope of :other.
301
- Expected the validation to be scoped to :other, but it was not scoped
302
- to anything.
303
- MESSAGE
304
-
305
- expect(&assertion).to fail_with_message(message)
306
- end
307
- end
308
- end
309
-
310
- context 'when the record is not created beforehand' do
311
- it 'creates the record automatically' do
312
- model = define_model_validating_uniqueness
313
- assertion = -> {
314
- record = build_record_from(model)
315
- expect(record).to validate_uniqueness
316
- }
317
- expect(&assertion).to change(model, :count).from(0).to(1)
318
- end
319
-
320
- context 'and the table has required attributes other than the attribute being validated, set beforehand' do
321
- it 'does not require the record to be persisted' do
322
- options = {
323
- additional_attributes: [
324
- { name: :required_attribute, options: { null: false } }
325
- ]
326
- }
327
- model = define_model_validating_uniqueness(options) do |m|
328
- m.validates_presence_of :required_attribute
329
- end
330
-
331
- record = build_record_from(model, required_attribute: 'something')
332
- expect(record).to validate_uniqueness
333
- end
334
- end
335
- end
336
-
337
- context 'and the validation has a custom message' do
338
- context 'when no message is specified' do
339
- it 'rejects with an appropriate failure message' do
340
- record = build_record_validating_uniqueness(
341
- attribute_value: 'some value',
342
- validation_options: { message: 'bad value' }
343
- )
344
-
345
- assertion = lambda do
346
- expect(record).to validate_uniqueness
347
- end
348
-
349
- message = <<-MESSAGE
350
- Example did not properly validate that :attr is case-sensitively unique.
351
- After taking the given Example, whose :attr is ‹"some value"›, and
352
- saving it as the existing record, then making a new Example and
353
- setting its :attr to ‹"some value"› as well, the matcher expected the
354
- new Example to be invalid and to produce the validation error "has
355
- already been taken" on :attr. The record was indeed invalid, but it
356
- produced these validation errors instead:
357
-
358
- * attr: ["bad value"]
359
- MESSAGE
360
-
361
- expect(&assertion).to fail_with_message(message)
362
- end
363
- end
364
-
365
- context 'given a string' do
366
- context 'when the given and actual messages do not match' do
367
- it 'rejects with an appropriate failure message' do
368
- record = build_record_validating_uniqueness(
369
- attribute_value: 'some value',
370
- validation_options: { message: 'something else entirely' }
371
- )
372
-
373
- assertion = lambda do
374
- expect(record).
375
- to validate_uniqueness.
376
- with_message('some message')
377
- end
378
-
379
- message = <<-MESSAGE
380
- Example did not properly validate that :attr is case-sensitively unique,
381
- producing a custom validation error on failure.
382
- After taking the given Example, whose :attr is ‹"some value"›, and
383
- saving it as the existing record, then making a new Example and
384
- setting its :attr to ‹"some value"› as well, the matcher expected the
385
- new Example to be invalid and to produce the validation error "some
386
- message" on :attr. The record was indeed invalid, but it produced
387
- these validation errors instead:
388
-
389
- * attr: ["something else entirely"]
390
- MESSAGE
391
-
392
- expect(&assertion).to fail_with_message(message)
393
- end
394
- end
395
-
396
- context 'when the given and actual messages match' do
397
- it 'accepts' do
398
- record = build_record_validating_uniqueness(
399
- validation_options: { message: 'bad value' }
400
- )
401
- expect(record).
402
- to validate_uniqueness.
403
- with_message('bad value')
404
- end
405
- end
406
- end
407
-
408
- context 'given a regex' do
409
- context 'when the given and actual messages do not match' do
410
- it 'rejects with an appropriate failure message' do
411
- record = build_record_validating_uniqueness(
412
- attribute_value: 'some value',
413
- validation_options: { message: 'something else entirely' }
414
- )
415
-
416
- assertion = lambda do
417
- expect(record).
418
- to validate_uniqueness.
419
- with_message(/some message/)
420
- end
421
-
422
- message = <<-MESSAGE
423
- Example did not properly validate that :attr is case-sensitively unique,
424
- producing a custom validation error on failure.
425
- After taking the given Example, whose :attr is ‹"some value"›, and
426
- saving it as the existing record, then making a new Example and
427
- setting its :attr to ‹"some value"› as well, the matcher expected the
428
- new Example to be invalid and to produce a validation error matching
429
- ‹/some message/› on :attr. The record was indeed invalid, but it
430
- produced these validation errors instead:
431
-
432
- * attr: ["something else entirely"]
433
- MESSAGE
434
-
435
- expect(&assertion).to fail_with_message(message)
436
- end
437
- end
438
-
439
- context 'when the given and actual messages match' do
440
- it 'accepts' do
441
- record = build_record_validating_uniqueness(
442
- validation_options: { message: 'bad value' }
443
- )
444
- expect(record).
445
- to validate_uniqueness.
446
- with_message(/bad/)
447
- end
448
- end
449
- end
450
- end
451
-
452
- it_supports(
453
- 'ignoring_interference_by_writer',
454
- tests: {
455
- reject_if_qualified_but_changing_value_interferes: {
456
- model_name: 'Example',
457
- attribute_name: :attr,
458
- default_value: 'some value',
459
- changing_values_with: :next_value,
460
- expected_message: <<-MESSAGE.strip
461
- Example did not properly validate that :attr is case-sensitively unique.
462
- After taking the given Example, whose :attr is ‹"some valuf"›, and
463
- saving it as the existing record, then making a new Example and
464
- setting its :attr to ‹"some valuf"› (read back as ‹"some valug"›) as
465
- well, the matcher expected the new Example to be invalid, but it was
466
- valid instead.
467
-
468
- As indicated in the message above, :attr seems to be changing certain
469
- values as they are set, and this could have something to do with why
470
- this test is failing. If you or something else has overridden the
471
- writer method for this attribute to normalize values by changing their
472
- case in any way (for instance, ensuring that the attribute is always
473
- downcased), then try adding `ignoring_case_sensitivity` onto the end
474
- of the uniqueness matcher. Otherwise, you may need to write the test
475
- yourself, or do something different altogether.
476
-
477
- MESSAGE
478
- }
479
- }
480
- )
481
- end
482
-
483
- context 'when the model has a scoped uniqueness validation' do
484
- context 'when one of the scoped attributes is a string column' do
485
- include_context 'it supports scoped attributes of a certain type',
486
- column_type: :string
487
- end
488
-
489
- context 'when one of the scoped attributes is a boolean column' do
490
- include_context 'it supports scoped attributes of a certain type',
491
- column_type: :boolean
492
- end
493
-
494
- context 'when there is more than one scoped attribute and all are boolean columns' do
495
- it 'accepts when all of the scoped attributes are true' do
496
- record = build_record_validating_uniqueness(
497
- scopes: [
498
- { type: :boolean, name: :scope1, value: true },
499
- { type: :boolean, name: :scope2, value: true }
500
- ]
501
- )
502
- expect(record).to validate_uniqueness.scoped_to(:scope1, :scope2)
503
- end
504
-
505
- it 'accepts when all the scoped attributes are false' do
506
- record = build_record_validating_uniqueness(
507
- scopes: [
508
- { type: :boolean, name: :scope1, value: false },
509
- { type: :boolean, name: :scope2, value: false }
510
- ]
511
- )
512
- expect(record).to validate_uniqueness.scoped_to(:scope1, :scope2)
513
- end
514
-
515
- it 'accepts when one of the scoped attributes is true and the other is false' do
516
- record = build_record_validating_uniqueness(
517
- scopes: [
518
- { type: :boolean, name: :scope1, value: true },
519
- { type: :boolean, name: :scope2, value: false }
520
- ]
521
- )
522
- expect(record).to validate_uniqueness.scoped_to(:scope1, :scope2)
523
- end
524
- end
525
-
526
- context 'when one of the scoped attributes is an integer column' do
527
- include_context 'it supports scoped attributes of a certain type',
528
- column_type: :integer
529
-
530
- if active_record_supports_enum?
531
- context 'when one of the scoped attributes is an enum' do
532
- it 'accepts' do
533
- record = build_record_validating_scoped_uniqueness_with_enum(
534
- enum_scope: :scope
535
- )
536
- expect(record).to validate_uniqueness.scoped_to(:scope)
537
- end
538
-
539
- context 'when too narrow of a scope is specified' do
540
- it 'rejects with an appropriate failure message' do
541
- record = build_record_validating_scoped_uniqueness_with_enum(
542
- enum_scope: :scope1,
543
- additional_scopes: [:scope2],
544
- additional_attributes: [:other]
545
- )
546
-
547
- assertion = lambda do
548
- expect(record).
549
- to validate_uniqueness.
550
- scoped_to(:scope1, :scope2, :other)
551
- end
552
-
553
- message = <<-MESSAGE
554
- Example did not properly validate that :attr is case-sensitively unique
555
- within the scope of :scope1, :scope2, and :other.
556
- Expected the validation to be scoped to :scope1, :scope2, and :other,
557
- but it was scoped to :scope1 and :scope2 instead.
558
- MESSAGE
559
-
560
- expect(&assertion).to fail_with_message(message)
561
- end
562
- end
563
-
564
- context 'when too broad of a scope is specified' do
565
- it 'rejects with an appropriate failure message' do
566
- record = build_record_validating_scoped_uniqueness_with_enum(
567
- enum_scope: :scope1,
568
- additional_scopes: [:scope2]
569
- )
570
-
571
- assertion = lambda do
572
- expect(record).to validate_uniqueness.scoped_to(:scope1)
573
- end
574
-
575
- message = <<-MESSAGE
576
- Example did not properly validate that :attr is case-sensitively unique
577
- within the scope of :scope1.
578
- Expected the validation to be scoped to :scope1, but it was scoped to
579
- :scope1 and :scope2 instead.
580
- MESSAGE
581
-
582
- expect(&assertion).to fail_with_message(message)
583
- end
584
- end
585
- end
586
- end
587
- end
588
-
589
- context 'when one of the scoped attributes is a date column' do
590
- include_context 'it supports scoped attributes of a certain type',
591
- column_type: :date
592
- end
593
-
594
- context 'when one of the scoped attributes is a datetime column (using DateTime)' do
595
- include_context 'it supports scoped attributes of a certain type',
596
- column_type: :datetime
597
- end
598
-
599
- context 'when one of the scoped attributes is a datetime column (using Time)' do
600
- include_context 'it supports scoped attributes of a certain type',
601
- column_type: :datetime,
602
- value_type: :time
603
- end
604
-
605
- context 'when one of the scoped attributes is a text column' do
606
- include_context 'it supports scoped attributes of a certain type',
607
- column_type: :text
608
- end
609
-
610
- if database_supports_uuid_columns?
611
- context 'when one of the scoped attributes is a UUID column' do
612
- include_context 'it supports scoped attributes of a certain type',
613
- column_type: :uuid
614
- end
615
- end
616
-
617
- if database_supports_array_columns? && active_record_supports_array_columns?
618
- context 'when one of the scoped attributes is a array-of-string column' do
619
- include_examples 'it supports scoped attributes of a certain type',
620
- column_type: :string,
621
- array: true
622
- end
623
-
624
- context 'when one of the scoped attributes is an array-of-integer column' do
625
- include_examples 'it supports scoped attributes of a certain type',
626
- column_type: :integer,
627
- array: true
628
- end
629
-
630
- context 'when one of the scoped attributes is an array-of-date column' do
631
- include_examples 'it supports scoped attributes of a certain type',
632
- column_type: :date,
633
- array: true
634
- end
635
-
636
- context 'when one of the scoped attributes is an array-of-datetime column (using DateTime)' do
637
- include_examples 'it supports scoped attributes of a certain type',
638
- column_type: :datetime,
639
- array: true
640
- end
641
-
642
- context 'when one of the scoped attributes is an array-of-datetime column (using Time)' do
643
- include_examples 'it supports scoped attributes of a certain type',
644
- column_type: :datetime,
645
- value_type: :time,
646
- array: true
647
- end
648
-
649
- context 'when one of the scoped attributes is an array-of-text column' do
650
- include_examples 'it supports scoped attributes of a certain type',
651
- column_type: :text,
652
- array: true
653
- end
654
- end
655
-
656
- context "when an existing record that is not the first has a nil value for the scoped attribute" do
657
- it 'still works' do
658
- model = define_model_validating_uniqueness(scopes: [:scope])
659
- create_record_from(model, scope: 'some value')
660
- create_record_from(model, scope: nil)
661
- record = build_record_from(model, scope: 'a different value')
662
-
663
- expect(record).to validate_uniqueness.scoped_to(:scope)
664
- end
665
- end
666
- end
667
-
668
- context 'when the model has a case-sensitive validation' do
669
- context 'when the matcher is not qualified with case_insensitive' do
670
- it 'accepts' do
671
- record = build_record_validating_uniqueness(
672
- attribute_type: :string,
673
- validation_options: { case_sensitive: true }
674
- )
675
-
676
- expect(record).to validate_uniqueness
677
- end
678
-
679
- context 'given an existing record where the value of the attribute under test is not case-swappable' do
680
- it 'raises a NonCaseSwappableValueError' do
681
- model = define_model_validating_uniqueness(
682
- attribute_type: :string,
683
- validation_options: { case_sensitive: true },
684
- )
685
- record = create_record_from(model, attribute_name => '123')
686
- running_matcher = -> { validate_uniqueness.matches?(record) }
687
-
688
- expect(&running_matcher).
689
- to raise_error(described_class::NonCaseSwappableValueError)
690
- end
691
- end
692
- end
693
-
694
- context 'when the matcher is qualified with case_insensitive' do
695
- it 'rejects with an appropriate failure message' do
696
- record = build_record_validating_uniqueness(
697
- attribute_type: :string,
698
- attribute_value: 'some value',
699
- validation_options: { case_sensitive: true }
700
- )
701
-
702
- assertion = lambda do
703
- expect(record).to validate_uniqueness.case_insensitive
704
- end
705
-
706
- message = <<-MESSAGE
707
- Example did not properly validate that :attr is case-insensitively
708
- unique.
709
- After taking the given Example, whose :attr is ‹"some value"›, and
710
- saving it as the existing record, then making a new Example and
711
- setting its :attr to a different value, ‹"SOME VALUE"›, the matcher
712
- expected the new Example to be invalid, but it was valid instead.
713
- MESSAGE
714
-
715
- expect(&assertion).to fail_with_message(message)
716
- end
717
- end
718
- end
719
-
720
- context 'when the model has a case-insensitive validation' do
721
- context 'when case_insensitive is not specified' do
722
- it 'rejects with an appropriate failure message' do
723
- record = build_record_validating_uniqueness(
724
- attribute_type: :string,
725
- validation_options: { case_sensitive: false }
726
- )
727
-
728
- assertion = lambda do
729
- expect(record).to validate_uniqueness
730
- end
731
-
732
- message = <<-MESSAGE
733
- Example did not properly validate that :attr is case-sensitively unique.
734
- After taking the given Example, setting its :attr to ‹"an arbitrary
735
- value"›, and saving it as the existing record, then making a new
736
- Example and setting its :attr to a different value, ‹"AN ARBITRARY
737
- VALUE"›, the matcher expected the new Example to be valid, but it was
738
- invalid instead, producing these validation errors:
739
-
740
- * attr: ["has already been taken"]
741
- MESSAGE
742
-
743
- expect(&assertion).to fail_with_message(message)
744
- end
745
- end
746
-
747
- context 'when case_insensitive is specified' do
748
- it 'accepts' do
749
- record = build_record_validating_uniqueness(
750
- attribute_type: :string,
751
- validation_options: { case_sensitive: false }
752
- )
753
-
754
- expect(record).to validate_uniqueness.case_insensitive
755
- end
756
-
757
- it_supports(
758
- 'ignoring_interference_by_writer',
759
- tests: {
760
- reject_if_qualified_but_changing_value_interferes: {
761
- model_name: 'Example',
762
- attribute_name: :attr,
763
- default_value: 'some value',
764
- changing_values_with: :next_value,
765
- expected_message: <<-MESSAGE.strip
766
- Example did not properly validate that :attr is case-insensitively
767
- unique.
768
- After taking the given Example, whose :attr is ‹"some valuf"›, and
769
- saving it as the existing record, then making a new Example and
770
- setting its :attr to ‹"some valuf"› (read back as ‹"some valug"›) as
771
- well, the matcher expected the new Example to be invalid, but it was
772
- valid instead.
773
-
774
- As indicated in the message above, :attr seems to be changing certain
775
- values as they are set, and this could have something to do with why
776
- this test is failing. If you or something else has overridden the
777
- writer method for this attribute to normalize values by changing their
778
- case in any way (for instance, ensuring that the attribute is always
779
- downcased), then try adding `ignoring_case_sensitivity` onto the end
780
- of the uniqueness matcher. Otherwise, you may need to write the test
781
- yourself, or do something different altogether.
782
- MESSAGE
783
- }
784
- }
785
- )
786
-
787
- def validation_matcher_scenario_args
788
- super.deep_merge(validation_options: { case_sensitive: false })
789
- end
790
-
791
- def configure_validation_matcher(matcher)
792
- super(matcher).case_insensitive
793
- end
794
- end
795
- end
796
-
797
- context 'when the validation is declared with allow_nil' do
798
- context 'given a new record whose attribute is nil' do
799
- it 'accepts' do
800
- model = define_model_validating_uniqueness(
801
- validation_options: { allow_nil: true }
802
- )
803
- record = build_record_from(model, attribute_name => nil)
804
- expect(record).to validate_uniqueness.allow_nil
805
- end
806
- end
807
-
808
- context 'given an existing record whose attribute is nil' do
809
- it 'accepts' do
810
- model = define_model_validating_uniqueness(
811
- validation_options: { allow_nil: true }
812
- )
813
- record = create_record_from(model, attribute_name => nil)
814
- expect(record).to validate_uniqueness.allow_nil
815
- end
816
- end
817
-
818
- if active_record_supports_has_secure_password?
819
- context 'when the model is declared with has_secure_password' do
820
- it 'accepts' do
821
- model = define_model_validating_uniqueness(
822
- validation_options: { allow_nil: true },
823
- additional_attributes: [{ name: :password_digest, type: :string }]
824
- ) do |m|
825
- m.has_secure_password
826
- end
827
-
828
- record = build_record_from(model, attribute_name => nil)
829
-
830
- expect(record).to validate_uniqueness.allow_nil
831
- end
832
- end
833
- end
834
- end
835
-
836
- context 'when the validation is not declared with allow_nil' do
837
- context 'given a new record whose attribute is nil' do
838
- it 'rejects with an appropriate failure message' do
839
- model = define_model_validating_uniqueness
840
- record = build_record_from(model, attribute_name => nil)
841
-
842
- assertion = lambda do
843
- expect(record).to validate_uniqueness.allow_nil
844
- end
845
-
846
- message = <<-MESSAGE
847
- Example did not properly validate that :attr is case-sensitively unique,
848
- but only if it is not nil.
849
- After taking the given Example, setting its :attr to ‹nil›, and saving
850
- it as the existing record, then making a new Example and setting its
851
- :attr to ‹nil› as well, the matcher expected the new Example to be
852
- valid, but it was invalid instead, producing these validation errors:
853
-
854
- * attr: ["has already been taken"]
855
- MESSAGE
856
-
857
- expect(&assertion).to fail_with_message(message)
858
- end
859
- end
860
-
861
- context 'given an existing record whose attribute is nil' do
862
- it 'rejects with an appropriate failure message' do
863
- model = define_model_validating_uniqueness
864
- record = create_record_from(model, attribute_name => nil)
865
-
866
- assertion = lambda do
867
- expect(record).to validate_uniqueness.allow_nil
868
- end
869
-
870
- message = <<-MESSAGE
871
- Example did not properly validate that :attr is case-sensitively unique,
872
- but only if it is not nil.
873
- Given an existing Example, after setting its :attr to ‹nil›, then
874
- making a new Example and setting its :attr to ‹nil› as well, the
875
- matcher expected the new Example to be valid, but it was invalid
876
- instead, producing these validation errors:
877
-
878
- * attr: ["has already been taken"]
879
- MESSAGE
880
-
881
- expect(&assertion).to fail_with_message(message)
882
- end
883
- end
884
- end
885
-
886
- context 'when the validation is declared with allow_blank' do
887
- context 'given a new record whose attribute is nil' do
888
- it 'accepts' do
889
- model = define_model_validating_uniqueness(
890
- validation_options: { allow_blank: true }
891
- )
892
- record = build_record_from(model, attribute_name => nil)
893
- expect(record).to validate_uniqueness.allow_blank
894
- end
895
- end
896
-
897
- context 'given an existing record whose attribute is nil' do
898
- it 'accepts' do
899
- model = define_model_validating_uniqueness(
900
- validation_options: { allow_blank: true }
901
- )
902
- record = create_record_from(model, attribute_name => nil)
903
- expect(record).to validate_uniqueness.allow_blank
904
- end
905
- end
906
-
907
- context 'given a new record whose attribute is empty' do
908
- it 'accepts' do
909
- model = define_model_validating_uniqueness(
910
- attribute_type: :string,
911
- validation_options: { allow_blank: true }
912
- )
913
- record = build_record_from(model, attribute_name => '')
914
- expect(record).to validate_uniqueness.allow_blank
915
- end
916
- end
917
-
918
- context 'given an existing record whose attribute is empty' do
919
- it 'accepts' do
920
- model = define_model_validating_uniqueness(
921
- attribute_type: :string,
922
- validation_options: { allow_blank: true }
923
- )
924
- record = create_record_from(model, attribute_name => '')
925
- expect(record).to validate_uniqueness.allow_blank
926
- end
927
- end
928
-
929
- if active_record_supports_has_secure_password?
930
- context 'when the model is declared with has_secure_password' do
931
- context 'given a record whose attribute is nil' do
932
- it 'accepts' do
933
- model = define_model_validating_uniqueness(
934
- validation_options: { allow_blank: true },
935
- additional_attributes: [{ name: :password_digest, type: :string }]
936
- ) do |m|
937
- m.has_secure_password
938
- end
939
-
940
- record = build_record_from(model, attribute_name => nil)
941
-
942
- expect(record).to validate_uniqueness.allow_blank
943
- end
944
- end
945
-
946
- context 'given a record whose attribute is empty' do
947
- it 'accepts' do
948
- model = define_model_validating_uniqueness(
949
- attribute_type: :string,
950
- validation_options: { allow_blank: true },
951
- additional_attributes: [{ name: :password_digest, type: :string }]
952
- ) do |m|
953
- m.has_secure_password
954
- end
955
-
956
- record = build_record_from(model, attribute_name => '')
957
-
958
- expect(record).to validate_uniqueness.allow_blank
959
- end
960
- end
961
- end
962
- end
963
- end
964
-
965
- context 'when the validation is not declared with allow_blank' do
966
- context 'given a new record whose attribute is nil' do
967
- it 'rejects with an appropriate failure message' do
968
- model = define_model_validating_uniqueness
969
- record = build_record_from(model, attribute_name => nil)
970
-
971
- assertion = lambda do
972
- expect(record).to validate_uniqueness.allow_blank
973
- end
974
-
975
- message = <<-MESSAGE
976
- Example did not properly validate that :attr is case-sensitively unique,
977
- but only if it is not blank.
978
- After taking the given Example, setting its :attr to ‹""›, and saving
979
- it as the existing record, then making a new Example and setting its
980
- :attr to ‹""› as well, the matcher expected the new Example to be
981
- valid, but it was invalid instead, producing these validation errors:
982
-
983
- * attr: ["has already been taken"]
984
- MESSAGE
985
-
986
- expect(&assertion).to fail_with_message(message)
987
- end
988
- end
989
-
990
- context 'given an existing record whose attribute is nil' do
991
- it 'rejects with an appropriate failure message' do
992
- model = define_model_validating_uniqueness
993
- record = create_record_from(model, attribute_name => nil)
994
-
995
- assertion = lambda do
996
- expect(record).to validate_uniqueness.allow_blank
997
- end
998
-
999
- message = <<-MESSAGE
1000
- Example did not properly validate that :attr is case-sensitively unique,
1001
- but only if it is not blank.
1002
- Given an existing Example, after setting its :attr to ‹""›, then
1003
- making a new Example and setting its :attr to ‹""› as well, the
1004
- matcher expected the new Example to be valid, but it was invalid
1005
- instead, producing these validation errors:
1006
-
1007
- * attr: ["has already been taken"]
1008
- MESSAGE
1009
-
1010
- expect(&assertion).to fail_with_message(message)
1011
- end
1012
- end
1013
-
1014
- context 'given a new record whose attribute is empty' do
1015
- it 'rejects with an appropriate failure message' do
1016
- model = define_model_validating_uniqueness(
1017
- attribute_type: :string
1018
- )
1019
- record = build_record_from(model, attribute_name => '')
1020
-
1021
- assertion = lambda do
1022
- expect(record).to validate_uniqueness.allow_blank
1023
- end
1024
-
1025
- message = <<-MESSAGE
1026
- Example did not properly validate that :attr is case-sensitively unique,
1027
- but only if it is not blank.
1028
- After taking the given Example, setting its :attr to ‹""›, and saving
1029
- it as the existing record, then making a new Example and setting its
1030
- :attr to ‹""› as well, the matcher expected the new Example to be
1031
- valid, but it was invalid instead, producing these validation errors:
1032
-
1033
- * attr: ["has already been taken"]
1034
- MESSAGE
1035
-
1036
- expect(&assertion).to fail_with_message(message)
1037
- end
1038
- end
1039
-
1040
- context 'given an existing record whose attribute is empty' do
1041
- it 'rejects with an appropriate failure message' do
1042
- model = define_model_validating_uniqueness(
1043
- attribute_type: :string
1044
- )
1045
- record = create_record_from(model, attribute_name => '')
1046
-
1047
- assertion = lambda do
1048
- expect(record).to validate_uniqueness.allow_blank
1049
- end
1050
-
1051
- message = <<-MESSAGE
1052
- Example did not properly validate that :attr is case-sensitively unique,
1053
- but only if it is not blank.
1054
- Given an existing Example, after setting its :attr to ‹""›, then
1055
- making a new Example and setting its :attr to ‹""› as well, the
1056
- matcher expected the new Example to be valid, but it was invalid
1057
- instead, producing these validation errors:
1058
-
1059
- * attr: ["has already been taken"]
1060
- MESSAGE
1061
-
1062
- expect(&assertion).to fail_with_message(message)
1063
- end
1064
- end
1065
- end
1066
-
1067
- context 'when testing that a polymorphic *_type column is one of the validation scopes' do
1068
- it 'sets that column to a meaningful value that works with other validations on the same column' do
1069
- user_model = define_model 'User'
1070
- favorite_columns = {
1071
- favoriteable_id: { type: :integer, options: { null: false } },
1072
- favoriteable_type: { type: :string, options: { null: false } }
1073
- }
1074
- favorite_model = define_model 'Favorite', favorite_columns do
1075
- attr_accessible :favoriteable
1076
- belongs_to :favoriteable, polymorphic: true
1077
- validates :favoriteable, presence: true
1078
- validates :favoriteable_id, uniqueness: { scope: :favoriteable_type }
1079
- end
1080
-
1081
- user = user_model.create!
1082
- favorite_model.create!(favoriteable: user)
1083
- new_favorite = favorite_model.new
1084
-
1085
- expect(new_favorite).
1086
- to validate_uniqueness_of(:favoriteable_id).
1087
- scoped_to(:favoriteable_type)
1088
- end
1089
-
1090
- context 'if the model the *_type column refers to is namespaced, and shares the last part of its name with an existing model' do
1091
- it 'still works' do
1092
- define_class 'User'
1093
- define_module 'Models'
1094
- user_model = define_model 'Models::User'
1095
- favorite_columns = {
1096
- favoriteable_id: { type: :integer, options: { null: false } },
1097
- favoriteable_type: { type: :string, options: { null: false } }
1098
- }
1099
- favorite_model = define_model 'Models::Favorite', favorite_columns do
1100
- attr_accessible :favoriteable
1101
- belongs_to :favoriteable, polymorphic: true
1102
- validates :favoriteable, presence: true
1103
- validates :favoriteable_id, uniqueness: { scope: :favoriteable_type }
1104
- end
1105
-
1106
- user = user_model.create!
1107
- favorite_model.create!(favoriteable: user)
1108
- new_favorite = favorite_model.new
1109
-
1110
- expect(new_favorite).
1111
- to validate_uniqueness_of(:favoriteable_id).
1112
- scoped_to(:favoriteable_type)
1113
- end
1114
- end
1115
- end
1116
-
1117
- context 'when the model does not have the attribute being tested' do
1118
- it 'fails with an appropriate failure message' do
1119
- model = define_model(:example)
1120
-
1121
- assertion = lambda do
1122
- expect(model.new).to validate_uniqueness_of(:attr)
1123
- end
1124
-
1125
- message = <<-MESSAGE.strip
1126
- Example did not properly validate that :attr is case-sensitively unique.
1127
- :attr does not seem to be an attribute on Example.
1128
- MESSAGE
1129
-
1130
- expect(&assertion).to fail_with_message(message)
1131
- end
1132
- end
1133
-
1134
- context 'when the writer method for the attribute changes the case of incoming values' do
1135
- context 'when the validation is case-sensitive' do
1136
- context 'and the matcher is ensuring that the validation is case-sensitive' do
1137
- it 'rejects with an appropriate failure message' do
1138
- model = define_model_validating_uniqueness(
1139
- attribute_name: :name
1140
- )
1141
-
1142
- model.class_eval do
1143
- def name=(name)
1144
- super(name.upcase)
1145
- end
1146
- end
1147
-
1148
- assertion = lambda do
1149
- expect(model.new).to validate_uniqueness_of(:name)
1150
- end
1151
-
1152
- message = <<-MESSAGE.strip
1153
- Example did not properly validate that :name is case-sensitively unique.
1154
- After taking the given Example, setting its :name to ‹"an arbitrary
1155
- value"› (read back as ‹"AN ARBITRARY VALUE"›), and saving it as the
1156
- existing record, then making a new Example and setting its :name to
1157
- ‹"an arbitrary value"› (read back as ‹"AN ARBITRARY VALUE"›) as well,
1158
- the matcher expected the new Example to be valid, but it was invalid
1159
- instead, producing these validation errors:
1160
-
1161
- * name: ["has already been taken"]
1162
-
1163
- As indicated in the message above, :name seems to be changing certain
1164
- values as they are set, and this could have something to do with why
1165
- this test is failing. If you or something else has overridden the
1166
- writer method for this attribute to normalize values by changing their
1167
- case in any way (for instance, ensuring that the attribute is always
1168
- downcased), then try adding `ignoring_case_sensitivity` onto the end
1169
- of the uniqueness matcher. Otherwise, you may need to write the test
1170
- yourself, or do something different altogether.
1171
- MESSAGE
1172
-
1173
- expect(&assertion).to fail_with_message(message)
1174
- end
1175
- end
1176
-
1177
- context 'and the matcher is ignoring case sensitivity' do
1178
- it 'accepts (and not raise an error)' do
1179
- model = define_model_validating_uniqueness(
1180
- attribute_name: :name
1181
- )
1182
-
1183
- model.class_eval do
1184
- def name=(name)
1185
- super(name.upcase)
1186
- end
1187
- end
1188
-
1189
- expect(model.new).
1190
- to validate_uniqueness_of(:name).
1191
- ignoring_case_sensitivity
1192
- end
1193
- end
1194
- end
1195
-
1196
- context 'when the validation is case-insensitive' do
1197
- context 'and the matcher is ensuring that the validation is case-insensitive' do
1198
- it 'accepts (and does not raise an error)' do
1199
- model = define_model_validating_uniqueness(
1200
- attribute_name: :name,
1201
- validation_options: { case_sensitive: false },
1202
- )
1203
-
1204
- model.class_eval do
1205
- def name=(name)
1206
- super(name.downcase)
1207
- end
1208
- end
1209
-
1210
- expect(model.new).
1211
- to validate_uniqueness_of(:name).
1212
- case_insensitive
1213
- end
1214
- end
1215
- end
1216
- end
1217
-
1218
- let(:model_attributes) { {} }
1219
-
1220
- def default_attribute
1221
- {
1222
- value_type: :string,
1223
- column_type: :string,
1224
- options: { array: false, null: true }
1225
- }
1226
- end
1227
-
1228
- def normalize_attribute(attribute)
1229
- if attribute.is_a?(Hash)
1230
- attribute_copy = attribute.dup
1231
-
1232
- if attribute_copy.key?(:type)
1233
- attribute_copy[:value_type] = attribute_copy[:type]
1234
- attribute_copy[:column_type] = attribute_copy[:type]
1235
- end
1236
-
1237
- default_attribute.deep_merge(attribute_copy)
1238
- else
1239
- default_attribute.deep_merge(name: attribute)
1240
- end
1241
- end
1242
-
1243
- def normalize_attributes(attributes)
1244
- attributes.map do |attribute|
1245
- normalize_attribute(attribute)
1246
- end
1247
- end
1248
-
1249
- def column_options_from(attributes)
1250
- attributes.inject({}) do |options, attribute|
1251
- options[attribute[:name]] = {
1252
- type: attribute[:column_type],
1253
- options: attribute.fetch(:options, {})
1254
- }
1255
- options
1256
- end
1257
- end
1258
-
1259
- def attributes_with_values_for(model)
1260
- model_attributes[model].each_with_object({}) do |attribute, attrs|
1261
- attrs[attribute[:name]] = attribute.fetch(:value) do
1262
- if attribute[:options][:null]
1263
- nil
1264
- else
1265
- dummy_value_for(
1266
- attribute[:value_type],
1267
- array: attribute[:options][:array]
1268
- )
1269
- end
1270
- end
1271
- end
1272
- end
1273
-
1274
- def dummy_value_for(attribute_type, array: false)
1275
- if array
1276
- [ dummy_scalar_value_for(attribute_type) ]
1277
- else
1278
- dummy_scalar_value_for(attribute_type)
1279
- end
1280
- end
1281
-
1282
- def dummy_scalar_value_for(attribute_type)
1283
- case attribute_type
1284
- when :string, :text
1285
- 'dummy value'
1286
- when :integer
1287
- 1
1288
- when :date
1289
- Date.today
1290
- when :datetime
1291
- Date.today.to_datetime
1292
- when :time
1293
- Time.now
1294
- when :uuid
1295
- SecureRandom.uuid
1296
- when :boolean
1297
- true
1298
- else
1299
- raise ArgumentError, "Unknown type '#{attribute_type}'"
1300
- end
1301
- end
1302
-
1303
- def next_version_of(value, value_type)
1304
- if value.is_a?(Array)
1305
- [ next_version_of(value[0], value_type) ]
1306
- elsif value_type == :uuid
1307
- SecureRandom.uuid
1308
- elsif value.is_a?(Time)
1309
- value + 1
1310
- elsif value.in?([true, false])
1311
- !value
1312
- elsif value.respond_to?(:next)
1313
- value.next
1314
- end
1315
- end
1316
-
1317
- def build_record_from(model, extra_attributes = {})
1318
- attributes = attributes_with_values_for(model)
1319
- model.new(attributes.merge(extra_attributes))
1320
- end
1321
-
1322
- def create_record_from(model, extra_attributes = {})
1323
- build_record_from(model, extra_attributes).tap do |record|
1324
- record.save!
1325
- end
1326
- end
1327
-
1328
- def define_model_validating_uniqueness(options = {}, &block)
1329
- attribute_name = options.fetch(:attribute_name) { self.attribute_name }
1330
- attribute_type = options.fetch(:attribute_type, :string)
1331
- attribute_options = options.fetch(:attribute_options, {})
1332
- attribute = normalize_attribute(
1333
- name: attribute_name,
1334
- value_type: attribute_type,
1335
- column_type: attribute_type,
1336
- options: attribute_options
1337
- )
1338
-
1339
- if options.key?(:attribute_value)
1340
- attribute[:value] = options[:attribute_value]
1341
- end
1342
-
1343
- scope_attributes = normalize_attributes(options.fetch(:scopes, []))
1344
- scope_attribute_names = scope_attributes.map { |attr| attr[:name] }
1345
- additional_attributes = normalize_attributes(
1346
- options.fetch(:additional_attributes, [])
1347
- )
1348
- attributes = [attribute] + scope_attributes + additional_attributes
1349
- validation_options = options.fetch(:validation_options, {})
1350
- column_options = column_options_from(attributes)
1351
-
1352
- model = define_model(:example, column_options) do |m|
1353
- m.validates_uniqueness_of attribute_name,
1354
- validation_options.merge(scope: scope_attribute_names)
1355
-
1356
- attributes.each do |attr|
1357
- m.attr_accessible(attr[:name])
1358
- end
1359
-
1360
- block.call(m) if block
1361
- end
1362
-
1363
- model_attributes[model] = attributes
1364
-
1365
- model
1366
- end
1367
-
1368
- def build_record_validating_uniqueness(options = {}, &block)
1369
- model = define_model_validating_uniqueness(options, &block)
1370
- build_record_from(model)
1371
- end
1372
- alias_method :new_record_validating_uniqueness,
1373
- :build_record_validating_uniqueness
1374
-
1375
- def create_record_validating_uniqueness(options = {}, &block)
1376
- build_record_validating_uniqueness(options, &block).tap do |record|
1377
- record.save!
1378
- end
1379
- end
1380
- alias_method :existing_record_validating_uniqueness,
1381
- :create_record_validating_uniqueness
1382
-
1383
- def build_record_validating_scoped_uniqueness_with_enum(options = {})
1384
- options = options.dup
1385
- enum_scope_attribute =
1386
- normalize_attribute(options.delete(:enum_scope)).
1387
- merge(value_type: :integer, column_type: :integer)
1388
- additional_scopes = options.delete(:additional_scopes) { [] }
1389
- options[:scopes] = [enum_scope_attribute] + additional_scopes
1390
- dummy_enum_values = [:foo, :bar]
1391
-
1392
- model = define_model_validating_uniqueness(options)
1393
- model.enum(enum_scope_attribute[:name] => dummy_enum_values)
1394
-
1395
- build_record_from(model)
1396
- end
1397
-
1398
- def define_model_without_validation
1399
- define_model(:example, attribute_name => :string) do |model|
1400
- model.attr_accessible(attribute_name)
1401
- end
1402
- end
1403
-
1404
- def validate_uniqueness
1405
- validate_uniqueness_of(attribute_name)
1406
- end
1407
-
1408
- def attribute_name
1409
- :attr
1410
- end
1411
-
1412
- def validation_matcher_scenario_args
1413
- super.deep_merge(
1414
- matcher_name: :validate_uniqueness_of,
1415
- model_creator: :"active_record/uniqueness_matcher"
1416
- )
1417
- end
1418
- end