mcmire-shoulda-matchers 2.5.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 (164) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.travis.yml +32 -0
  4. data/.yardopts +7 -0
  5. data/Appraisals +45 -0
  6. data/CONTRIBUTING.md +41 -0
  7. data/Gemfile +31 -0
  8. data/Gemfile.lock +166 -0
  9. data/MIT-LICENSE +22 -0
  10. data/NEWS.md +299 -0
  11. data/README.md +163 -0
  12. data/Rakefile +116 -0
  13. data/doc_config/gh-pages/index.html.erb +9 -0
  14. data/doc_config/yard/setup.rb +22 -0
  15. data/doc_config/yard/templates/default/fulldoc/html/css/bootstrap.css +5967 -0
  16. data/doc_config/yard/templates/default/fulldoc/html/css/full_list.css +12 -0
  17. data/doc_config/yard/templates/default/fulldoc/html/css/global.css +45 -0
  18. data/doc_config/yard/templates/default/fulldoc/html/css/solarized.css +69 -0
  19. data/doc_config/yard/templates/default/fulldoc/html/css/style.css +283 -0
  20. data/doc_config/yard/templates/default/fulldoc/html/full_list.erb +32 -0
  21. data/doc_config/yard/templates/default/fulldoc/html/full_list_class.erb +1 -0
  22. data/doc_config/yard/templates/default/fulldoc/html/full_list_method.erb +8 -0
  23. data/doc_config/yard/templates/default/fulldoc/html/js/app.js +300 -0
  24. data/doc_config/yard/templates/default/fulldoc/html/js/full_list.js +1 -0
  25. data/doc_config/yard/templates/default/fulldoc/html/js/jquery.stickyheaders.js +289 -0
  26. data/doc_config/yard/templates/default/fulldoc/html/js/underscore.min.js +6 -0
  27. data/doc_config/yard/templates/default/fulldoc/html/setup.rb +8 -0
  28. data/doc_config/yard/templates/default/layout/html/breadcrumb.erb +14 -0
  29. data/doc_config/yard/templates/default/layout/html/fonts.erb +1 -0
  30. data/doc_config/yard/templates/default/layout/html/layout.erb +23 -0
  31. data/doc_config/yard/templates/default/layout/html/search.erb +13 -0
  32. data/doc_config/yard/templates/default/layout/html/setup.rb +8 -0
  33. data/doc_config/yard/templates/default/method_details/html/source.erb +10 -0
  34. data/doc_config/yard/templates/default/module/html/box_info.erb +31 -0
  35. data/features/rails_integration.feature +113 -0
  36. data/features/step_definitions/rails_steps.rb +162 -0
  37. data/features/support/env.rb +5 -0
  38. data/gemfiles/3.0.gemfile +24 -0
  39. data/gemfiles/3.0.gemfile.lock +150 -0
  40. data/gemfiles/3.1.gemfile +27 -0
  41. data/gemfiles/3.1.gemfile.lock +173 -0
  42. data/gemfiles/3.2.gemfile +27 -0
  43. data/gemfiles/3.2.gemfile.lock +171 -0
  44. data/gemfiles/4.0.0.gemfile +28 -0
  45. data/gemfiles/4.0.0.gemfile.lock +172 -0
  46. data/gemfiles/4.0.1.gemfile +28 -0
  47. data/gemfiles/4.0.1.gemfile.lock +172 -0
  48. data/lib/shoulda-matchers.rb +1 -0
  49. data/lib/shoulda/matchers.rb +11 -0
  50. data/lib/shoulda/matchers/action_controller.rb +17 -0
  51. data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +64 -0
  52. data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +97 -0
  53. data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +81 -0
  54. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +117 -0
  55. data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +114 -0
  56. data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +154 -0
  57. data/lib/shoulda/matchers/action_controller/route_matcher.rb +116 -0
  58. data/lib/shoulda/matchers/action_controller/route_params.rb +48 -0
  59. data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +164 -0
  60. data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +296 -0
  61. data/lib/shoulda/matchers/active_model.rb +30 -0
  62. data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +167 -0
  63. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +314 -0
  64. data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +46 -0
  65. data/lib/shoulda/matchers/active_model/ensure_exclusion_of_matcher.rb +160 -0
  66. data/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb +417 -0
  67. data/lib/shoulda/matchers/active_model/ensure_length_of_matcher.rb +337 -0
  68. data/lib/shoulda/matchers/active_model/errors.rb +10 -0
  69. data/lib/shoulda/matchers/active_model/exception_message_finder.rb +58 -0
  70. data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +92 -0
  71. data/lib/shoulda/matchers/active_model/helpers.rb +46 -0
  72. data/lib/shoulda/matchers/active_model/numericality_matchers.rb +9 -0
  73. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +75 -0
  74. data/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +27 -0
  75. data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +41 -0
  76. data/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +27 -0
  77. data/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +26 -0
  78. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +112 -0
  79. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +77 -0
  80. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +121 -0
  81. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +380 -0
  82. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +89 -0
  83. data/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb +372 -0
  84. data/lib/shoulda/matchers/active_model/validation_matcher.rb +97 -0
  85. data/lib/shoulda/matchers/active_model/validation_message_finder.rb +69 -0
  86. data/lib/shoulda/matchers/active_record.rb +22 -0
  87. data/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +204 -0
  88. data/lib/shoulda/matchers/active_record/association_matcher.rb +901 -0
  89. data/lib/shoulda/matchers/active_record/association_matchers.rb +9 -0
  90. data/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +41 -0
  91. data/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +41 -0
  92. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +81 -0
  93. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +65 -0
  94. data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +94 -0
  95. data/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +41 -0
  96. data/lib/shoulda/matchers/active_record/association_matchers/source_matcher.rb +41 -0
  97. data/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +63 -0
  98. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +261 -0
  99. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +149 -0
  100. data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +72 -0
  101. data/lib/shoulda/matchers/active_record/serialize_matcher.rb +181 -0
  102. data/lib/shoulda/matchers/assertion_error.rb +19 -0
  103. data/lib/shoulda/matchers/error.rb +6 -0
  104. data/lib/shoulda/matchers/integrations/rspec.rb +20 -0
  105. data/lib/shoulda/matchers/integrations/test_unit.rb +30 -0
  106. data/lib/shoulda/matchers/rails_shim.rb +50 -0
  107. data/lib/shoulda/matchers/version.rb +6 -0
  108. data/lib/shoulda/matchers/warn.rb +8 -0
  109. data/shoulda-matchers.gemspec +23 -0
  110. data/spec/shoulda/matchers/action_controller/filter_param_matcher_spec.rb +22 -0
  111. data/spec/shoulda/matchers/action_controller/redirect_to_matcher_spec.rb +42 -0
  112. data/spec/shoulda/matchers/action_controller/render_template_matcher_spec.rb +78 -0
  113. data/spec/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +63 -0
  114. data/spec/shoulda/matchers/action_controller/rescue_from_matcher_spec.rb +63 -0
  115. data/spec/shoulda/matchers/action_controller/respond_with_matcher_spec.rb +31 -0
  116. data/spec/shoulda/matchers/action_controller/route_matcher_spec.rb +70 -0
  117. data/spec/shoulda/matchers/action_controller/route_params_spec.rb +30 -0
  118. data/spec/shoulda/matchers/action_controller/set_session_matcher_spec.rb +51 -0
  119. data/spec/shoulda/matchers/action_controller/set_the_flash_matcher_spec.rb +153 -0
  120. data/spec/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +111 -0
  121. data/spec/shoulda/matchers/active_model/allow_value_matcher_spec.rb +170 -0
  122. data/spec/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +81 -0
  123. data/spec/shoulda/matchers/active_model/ensure_exclusion_of_matcher_spec.rb +95 -0
  124. data/spec/shoulda/matchers/active_model/ensure_inclusion_of_matcher_spec.rb +320 -0
  125. data/spec/shoulda/matchers/active_model/ensure_length_of_matcher_spec.rb +166 -0
  126. data/spec/shoulda/matchers/active_model/exception_message_finder_spec.rb +111 -0
  127. data/spec/shoulda/matchers/active_model/have_secure_password_matcher_spec.rb +20 -0
  128. data/spec/shoulda/matchers/active_model/helpers_spec.rb +158 -0
  129. data/spec/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +169 -0
  130. data/spec/shoulda/matchers/active_model/numericality_matchers/even_number_matcher_spec.rb +59 -0
  131. data/spec/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher_spec.rb +59 -0
  132. data/spec/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher_spec.rb +57 -0
  133. data/spec/shoulda/matchers/active_model/validate_absence_of_matcher_spec.rb +139 -0
  134. data/spec/shoulda/matchers/active_model/validate_acceptance_of_matcher_spec.rb +41 -0
  135. data/spec/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb +47 -0
  136. data/spec/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +331 -0
  137. data/spec/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +180 -0
  138. data/spec/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb +398 -0
  139. data/spec/shoulda/matchers/active_model/validation_message_finder_spec.rb +127 -0
  140. data/spec/shoulda/matchers/active_record/accept_nested_attributes_for_matcher_spec.rb +107 -0
  141. data/spec/shoulda/matchers/active_record/association_matcher_spec.rb +860 -0
  142. data/spec/shoulda/matchers/active_record/association_matchers/model_reflection_spec.rb +247 -0
  143. data/spec/shoulda/matchers/active_record/have_db_column_matcher_spec.rb +111 -0
  144. data/spec/shoulda/matchers/active_record/have_db_index_matcher_spec.rb +78 -0
  145. data/spec/shoulda/matchers/active_record/have_readonly_attributes_matcher_spec.rb +41 -0
  146. data/spec/shoulda/matchers/active_record/serialize_matcher_spec.rb +86 -0
  147. data/spec/spec_helper.rb +26 -0
  148. data/spec/support/active_model_versions.rb +13 -0
  149. data/spec/support/active_resource_builder.rb +29 -0
  150. data/spec/support/activemodel_helpers.rb +19 -0
  151. data/spec/support/capture_helpers.rb +19 -0
  152. data/spec/support/class_builder.rb +42 -0
  153. data/spec/support/controller_builder.rb +74 -0
  154. data/spec/support/fail_with_message_including_matcher.rb +33 -0
  155. data/spec/support/fail_with_message_matcher.rb +32 -0
  156. data/spec/support/i18n_faker.rb +10 -0
  157. data/spec/support/mailer_builder.rb +10 -0
  158. data/spec/support/model_builder.rb +81 -0
  159. data/spec/support/rails_versions.rb +18 -0
  160. data/spec/support/shared_examples/numerical_submatcher.rb +19 -0
  161. data/spec/support/shared_examples/numerical_type_submatcher.rb +17 -0
  162. data/spec/support/test_application.rb +120 -0
  163. data/yard.watchr +5 -0
  164. metadata +281 -0
@@ -0,0 +1,247 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shoulda::Matchers::ActiveRecord::AssociationMatchers::ModelReflection do
4
+ it 'delegates other methods to the given Reflection object' do
5
+ define_model(:country)
6
+ person_model = define_model(:person, country_id: :integer) do
7
+ belongs_to :country
8
+ end
9
+ delegate_reflection = person_model.reflect_on_association(:country)
10
+ delegate_reflection.stubs(foo: 'bar')
11
+ reflection = described_class.new(delegate_reflection)
12
+
13
+ expect(reflection.foo).to eq 'bar'
14
+ end
15
+
16
+ describe '#associated_class' do
17
+ it 'returns the model that the association refers to' do
18
+ define_model(:country)
19
+ person_model = define_model(:person, country_id: :integer) do
20
+ belongs_to :country
21
+ end
22
+ delegate_reflection = person_model.reflect_on_association(:country)
23
+ reflection = described_class.new(delegate_reflection)
24
+
25
+ expect(reflection.associated_class).to be Country
26
+ end
27
+ end
28
+
29
+ describe '#through?' do
30
+ it 'returns true if the reflection is for a has_many :through association' do
31
+ define_model(:city, person_id: :integer)
32
+ define_model(:person, country_id: :integer) do
33
+ has_many :cities
34
+ end
35
+ country_model = define_model(:country) do
36
+ has_many :people
37
+ has_many :cities, through: :people
38
+ end
39
+ delegate_reflection = country_model.reflect_on_association(:cities)
40
+ reflection = described_class.new(delegate_reflection)
41
+
42
+ expect(reflection).to be_through
43
+ end
44
+
45
+ it 'returns false if not' do
46
+ define_model(:person, country_id: :integer)
47
+ country_model = define_model(:country) do
48
+ has_many :people
49
+ end
50
+ delegate_reflection = country_model.reflect_on_association(:people)
51
+ reflection = described_class.new(delegate_reflection)
52
+
53
+ expect(reflection).not_to be_through
54
+ end
55
+ end
56
+
57
+ describe '#join_table' do
58
+ context 'when the association was defined with a :join_table option' do
59
+ it 'returns the value of the option' do
60
+ define_model(:person, country_id: :integer)
61
+ country_model = define_model(:country) do
62
+ has_and_belongs_to_many :people, join_table: 'foos'
63
+ end
64
+ delegate_reflection = country_model.reflect_on_association(:people)
65
+ reflection = described_class.new(delegate_reflection)
66
+
67
+ expect(reflection.join_table).to eq 'foos'
68
+ end
69
+ end
70
+
71
+ context 'when the association was not defined with :join_table' do
72
+ it 'returns the default join_table that ActiveRecord generates' do
73
+ define_model(:person, country_id: :integer)
74
+ country_model = define_model(:country) do
75
+ has_and_belongs_to_many :people
76
+ end
77
+ delegate_reflection = country_model.reflect_on_association(:people)
78
+ reflection = described_class.new(delegate_reflection)
79
+
80
+ expect(reflection.join_table).to eq 'countries_people'
81
+ end
82
+ end
83
+ end
84
+
85
+ describe '#association_relation' do
86
+ if rails_4_x?
87
+ context 'when the reflection object has a #scope method' do
88
+ context 'when the scope is a block' do
89
+ it 'executes the block in the context of an empty scope' do
90
+ define_model(:country, mood: :string)
91
+ person_model = define_model(:person, country_id: :integer) do
92
+ belongs_to :country, -> { where(mood: 'nice') }
93
+ end
94
+ delegate_reflection = person_model.reflect_on_association(:country)
95
+ reflection = described_class.new(delegate_reflection)
96
+
97
+ actual_sql = reflection.association_relation.to_sql
98
+ expected_sql = Country.where(mood: 'nice').to_sql
99
+ expect(actual_sql).to eq expected_sql
100
+ end
101
+ end
102
+
103
+ context 'when the scope is nil' do
104
+ it 'returns an empty scope' do
105
+ define_model(:country)
106
+ person_model = define_model(:person, country_id: :integer) do
107
+ belongs_to :country
108
+ end
109
+ delegate_reflection = person_model.reflect_on_association(:country)
110
+ reflection = described_class.new(delegate_reflection)
111
+
112
+ actual_sql = reflection.association_relation.to_sql
113
+ expected_sql = Country.all.to_sql
114
+ expect(actual_sql).to eq expected_sql
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ if rails_3_x?
121
+ context 'when the reflection object does not have a #scope method' do
122
+ context 'when the reflection options contain :conditions' do
123
+ it 'creates an ActiveRecord::Relation from the conditions' do
124
+ define_model(:country, mood: :string)
125
+ person_model = define_model(:person, country_id: :integer) do
126
+ belongs_to :country, conditions: { mood: 'nice' }
127
+ end
128
+ delegate_reflection = person_model.reflect_on_association(:country)
129
+ reflection = described_class.new(delegate_reflection)
130
+
131
+ actual_sql = reflection.association_relation.to_sql
132
+ expected_sql = Country.where(mood: 'nice').to_sql
133
+ expect(actual_sql).to eq expected_sql
134
+ end
135
+ end
136
+
137
+ context 'when the reflection options contain :order' do
138
+ it 'creates an ActiveRecord::Relation from the order' do
139
+ define_model(:person, country_id: :integer, age: :integer)
140
+ country_model = define_model(:country) do
141
+ has_many :people, order: 'age'
142
+ end
143
+ delegate_reflection = country_model.reflect_on_association(:people)
144
+ reflection = described_class.new(delegate_reflection)
145
+
146
+ actual_sql = reflection.association_relation.to_sql
147
+ expected_sql = Person.order('age').to_sql
148
+ expect(actual_sql).to eq expected_sql
149
+ end
150
+ end
151
+
152
+ context 'when the reflection options contain :include' do
153
+ it 'creates an ActiveRecord::Relation from the include' do
154
+ define_model(:city, country_id: :integer)
155
+ define_model(:country) do
156
+ has_many :cities
157
+ end
158
+ person_model = define_model(:person, country_id: :integer) do
159
+ belongs_to :country, include: :cities
160
+ end
161
+ delegate_reflection = person_model.reflect_on_association(:country)
162
+ reflection = described_class.new(delegate_reflection)
163
+
164
+ actual_includes = reflection.association_relation.includes_values
165
+ expected_includes = Country.includes(:cities).includes_values
166
+ expect(actual_includes).to eq expected_includes
167
+ end
168
+ end
169
+
170
+ context 'when the reflection options contain :group' do
171
+ it 'creates an ActiveRecord::Relation from the group' do
172
+ country_model = define_model(:country, mood: :string) do
173
+ has_many :people, group: 'age'
174
+ end
175
+ define_model(:person, country_id: :integer, age: :integer)
176
+ delegate_reflection = country_model.reflect_on_association(:people)
177
+ reflection = described_class.new(delegate_reflection)
178
+
179
+ actual_sql = reflection.association_relation.to_sql
180
+ expected_sql = Person.group('age').to_sql
181
+ expect(actual_sql).to eq expected_sql
182
+ end
183
+ end
184
+
185
+ context 'when the reflection options contain :having' do
186
+ it 'creates an ActiveRecord::Relation from the having' do
187
+ country_model = define_model(:country) do
188
+ has_many :people, having: 'country_id > 1'
189
+ end
190
+ define_model(:person, country_id: :integer)
191
+ delegate_reflection = country_model.reflect_on_association(:people)
192
+ reflection = described_class.new(delegate_reflection)
193
+
194
+ actual_sql = reflection.association_relation.to_sql
195
+ expected_sql = Person.having('country_id > 1').to_sql
196
+ expect(actual_sql).to eq expected_sql
197
+ end
198
+ end
199
+
200
+ context 'when the reflection options contain :limit' do
201
+ it 'creates an ActiveRecord::Relation from the limit' do
202
+ country_model = define_model(:country) do
203
+ has_many :people, limit: 10
204
+ end
205
+ define_model(:person, country_id: :integer)
206
+ delegate_reflection = country_model.reflect_on_association(:people)
207
+ reflection = described_class.new(delegate_reflection)
208
+
209
+ actual_sql = reflection.association_relation.to_sql
210
+ expected_sql = Person.limit(10).to_sql
211
+ expect(actual_sql).to eq expected_sql
212
+ end
213
+ end
214
+
215
+ context 'when the reflection options contain :offset' do
216
+ it 'creates an ActiveRecord::Relation from the offset' do
217
+ country_model = define_model(:country) do
218
+ has_many :people, offset: 5
219
+ end
220
+ define_model(:person, country_id: :integer)
221
+ delegate_reflection = country_model.reflect_on_association(:people)
222
+ reflection = described_class.new(delegate_reflection)
223
+
224
+ actual_sql = reflection.association_relation.to_sql
225
+ expected_sql = Person.offset(5).to_sql
226
+ expect(actual_sql).to eq expected_sql
227
+ end
228
+ end
229
+
230
+ context 'when the reflection options contain :select' do
231
+ it 'creates an ActiveRecord::Relation from the select' do
232
+ country_model = define_model(:country) do
233
+ has_many :people, select: 'age'
234
+ end
235
+ define_model(:person, country_id: :integer, age: :integer)
236
+ delegate_reflection = country_model.reflect_on_association(:people)
237
+ reflection = described_class.new(delegate_reflection)
238
+
239
+ actual_sql = reflection.association_relation.to_sql
240
+ expected_sql = Person.select('age').to_sql
241
+ expect(actual_sql).to eq expected_sql
242
+ end
243
+ end
244
+ end
245
+ end
246
+ end
247
+ end
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shoulda::Matchers::ActiveRecord::HaveDbColumnMatcher do
4
+ it 'accepts an existing database column' do
5
+ expect(model(nickname: :string)).to have_db_column(:nickname)
6
+ end
7
+
8
+ it 'rejects a nonexistent database column' do
9
+ expect(define_model(:employee).new).not_to have_db_column(:nickname)
10
+ end
11
+
12
+ context '#of_type' do
13
+ it 'accepts a column of correct type' do
14
+ expect(model(nickname: :string)).
15
+ to have_db_column(:nickname).of_type(:string)
16
+ end
17
+
18
+ it 'rejects a nonexistent database column' do
19
+ expect(define_model(:superhero).new).
20
+ not_to have_db_column(:nickname).of_type(:string)
21
+ end
22
+
23
+ it 'rejects a column of wrong type' do
24
+ expect(model(nickname: :integer)).
25
+ not_to have_db_column(:nickname).of_type(:string)
26
+ end
27
+ end
28
+
29
+ context 'with precision option' do
30
+ it 'accepts a column of correct precision' do
31
+ expect(with_table(:salary, :decimal, precision: 5)).
32
+ to have_db_column(:salary).with_options(precision: 5)
33
+ end
34
+
35
+ it 'rejects a column of wrong precision' do
36
+ expect(with_table(:salary, :decimal, precision: 6)).
37
+ not_to have_db_column(:salary).with_options(precision: 5)
38
+ end
39
+ end
40
+
41
+ context 'with limit option' do
42
+ it 'accepts a column of correct limit' do
43
+ expect(with_table(:email, :string, limit: 255)).
44
+ to have_db_column(:email).with_options(limit: 255)
45
+ end
46
+
47
+ it 'rejects a column of wrong limit' do
48
+ expect(with_table(:email, :string, limit: 100)).
49
+ not_to have_db_column(:email).with_options(limit: 255)
50
+ end
51
+ end
52
+
53
+ context 'with default option' do
54
+ it 'accepts a column with correct default' do
55
+ expect(with_table(:admin, :boolean, default: false)).
56
+ to have_db_column(:admin).with_options(default: false)
57
+ end
58
+
59
+ it 'rejects a column with wrong default' do
60
+ expect(with_table(:admin, :boolean, default: true)).
61
+ not_to have_db_column(:admin).with_options(default: false)
62
+ end
63
+ end
64
+
65
+ context 'with null option' do
66
+ it 'accepts a column of correct null' do
67
+ expect(with_table(:admin, :boolean, null: false)).
68
+ to have_db_column(:admin).with_options(null: false)
69
+ end
70
+
71
+ it 'rejects a column of wrong null' do
72
+ expect(with_table(:admin, :boolean, null: true)).
73
+ not_to have_db_column(:admin).with_options(null: false)
74
+ end
75
+ end
76
+
77
+ context 'with scale option' do
78
+ it 'accepts a column of correct scale' do
79
+ expect(with_table(:salary, :decimal, precision: 10, scale: 2)).
80
+ to have_db_column(:salary).with_options(scale: 2)
81
+ end
82
+
83
+ it 'rejects a column of wrong scale' do
84
+ expect(with_table(:salary, :decimal, precision: 10, scale: 4)).
85
+ not_to have_db_column(:salary).with_options(scale: 2)
86
+ end
87
+ end
88
+
89
+ context 'with primary option' do
90
+ it 'accepts a column that is primary' do
91
+ expect(with_table(:custom_id, :integer, primary: true)).
92
+ to have_db_column(:id).with_options(primary: true)
93
+ end
94
+
95
+ it 'rejects a column that is not primary' do
96
+ expect(with_table(:whatever, :integer, primary: false)).
97
+ not_to have_db_column(:whatever).with_options(primary: true)
98
+ end
99
+ end
100
+
101
+ def model(options = {})
102
+ define_model(:employee, options).new
103
+ end
104
+
105
+ def with_table(column_name, column_type, options)
106
+ create_table 'employees' do |table|
107
+ table.__send__(column_type, column_name, options)
108
+ end
109
+ define_model_class('Employee').new
110
+ end
111
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shoulda::Matchers::ActiveRecord::HaveDbIndexMatcher do
4
+ context 'have_db_index' do
5
+ it 'accepts an existing index' do
6
+ expect(with_index_on(:age)).to have_db_index(:age)
7
+ end
8
+
9
+ it 'rejects a nonexistent index' do
10
+ expect(define_model(:employee).new).not_to have_db_index(:age)
11
+ end
12
+ end
13
+
14
+ context 'have_db_index with unique option' do
15
+ it 'accepts an index of correct unique' do
16
+ expect(with_index_on(:ssn, unique: true)).
17
+ to have_db_index(:ssn).unique(true)
18
+ end
19
+
20
+ it 'rejects an index of wrong unique' do
21
+ expect(with_index_on(:ssn, unique: false)).
22
+ not_to have_db_index(:ssn).unique(true)
23
+ end
24
+ end
25
+
26
+ context 'have_db_index on multiple columns' do
27
+ it 'accepts an existing index' do
28
+ db_connection = create_table 'geocodings' do |table|
29
+ table.integer :geocodable_id
30
+ table.string :geocodable_type
31
+ end
32
+ db_connection.add_index :geocodings, [:geocodable_type, :geocodable_id]
33
+ expect(define_model_class('Geocoding').new).
34
+ to have_db_index([:geocodable_type, :geocodable_id])
35
+ end
36
+
37
+ it 'rejects a nonexistent index' do
38
+ create_table 'geocodings' do |table|
39
+ table.integer :geocodable_id
40
+ table.string :geocodable_type
41
+ end
42
+ expect(define_model_class('Geocoding').new).
43
+ not_to have_db_index([:geocodable_type, :geocodable_id])
44
+ end
45
+ end
46
+
47
+ it 'join columns with and when describing multiple columns' do
48
+ expect(have_db_index([:user_id, :post_id]).description).to match(/on columns user_id and post_id/)
49
+ end
50
+
51
+ it 'describes a unique index as unique' do
52
+ expect(have_db_index(:user_id).unique(true).description).to match(/a unique index/)
53
+ end
54
+
55
+ it 'describes a non-unique index as non-unique' do
56
+ expect(have_db_index(:user_id).unique(false).description).to match(/a non-unique index/)
57
+ end
58
+
59
+ it "does not display an index's uniqueness when it's not important" do
60
+ expect(have_db_index(:user_id).description).not_to match(/unique/)
61
+ end
62
+
63
+ it 'allows an IndexDefinition to have a truthy value for unique' do
64
+ index_definition = stub('ActiveRecord::ConnectionAdapters::IndexDefinition',
65
+ unique: 7, name: :age)
66
+ matcher = have_db_index(:age).unique(true)
67
+ matcher.stubs(matched_index: index_definition)
68
+
69
+ expect(with_index_on(:age)).to matcher
70
+ end
71
+
72
+ def with_index_on(column_name, index_options = {})
73
+ create_table 'employees' do |table|
74
+ table.integer column_name
75
+ end.add_index(:employees, column_name, index_options)
76
+ define_model_class('Employee').new
77
+ end
78
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shoulda::Matchers::ActiveRecord::HaveReadonlyAttributeMatcher do
4
+ context 'a read-only attribute' do
5
+ it 'accepts' do
6
+ expect(with_readonly_attr).to have_readonly_attribute(:attr)
7
+ end
8
+ end
9
+
10
+ context 'an attribute that is not part of the read-only set' do
11
+ it 'rejects being read-only' do
12
+ model = define_model :example, attr: :string, other: :string do
13
+ attr_readonly :attr
14
+ end.new
15
+
16
+ expect(model).not_to have_readonly_attribute(:other)
17
+ end
18
+ end
19
+
20
+ context 'an attribute on a class with no readonly attributes' do
21
+ it 'rejects being read-only' do
22
+ expect(define_model(:example, attr: :string).new).
23
+ not_to have_readonly_attribute(:attr)
24
+ end
25
+
26
+ it 'assigns a failure message' do
27
+ model = define_model(:example, attr: :string).new
28
+ matcher = have_readonly_attribute(:attr)
29
+
30
+ matcher.matches?(model)
31
+
32
+ expect(matcher.failure_message).not_to be_nil
33
+ end
34
+ end
35
+
36
+ def with_readonly_attr
37
+ define_model :example, attr: :string do
38
+ attr_readonly :attr
39
+ end.new
40
+ end
41
+ end