ibm_db 1.1.1-mswin32

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 (36) hide show
  1. data/CHANGES +155 -0
  2. data/LICENSE +18 -0
  3. data/MANIFEST +14 -0
  4. data/README +274 -0
  5. data/ext/Makefile.nt32 +181 -0
  6. data/ext/extconf.rb +58 -0
  7. data/ext/ibm_db.c +6553 -0
  8. data/ext/ruby_ibm_db.h +214 -0
  9. data/init.rb +42 -0
  10. data/lib/IBM_DB.rb +2 -0
  11. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +2218 -0
  12. data/lib/active_record/vendor/db2-i5-zOS.yaml +328 -0
  13. data/lib/mswin32/ibm_db.so +0 -0
  14. data/test/cases/adapter_test.rb +180 -0
  15. data/test/cases/associations/cascaded_eager_loading_test.rb +133 -0
  16. data/test/cases/associations/eager_test.rb +842 -0
  17. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +874 -0
  18. data/test/cases/associations/has_many_through_associations_test.rb +281 -0
  19. data/test/cases/associations/join_model_test.rb +801 -0
  20. data/test/cases/attribute_methods_test.rb +312 -0
  21. data/test/cases/base_test.rb +2114 -0
  22. data/test/cases/calculations_test.rb +346 -0
  23. data/test/cases/finder_test.rb +1092 -0
  24. data/test/cases/fixtures_test.rb +660 -0
  25. data/test/cases/migration_test.rb +1618 -0
  26. data/test/cases/schema_dumper_test.rb +197 -0
  27. data/test/cases/validations_test.rb +1604 -0
  28. data/test/connections/native_ibm_db/connection.rb +40 -0
  29. data/test/ibm_db_test.rb +25 -0
  30. data/test/models/warehouse_thing.rb +5 -0
  31. data/test/schema/i5/ibm_db_specific_schema.rb +134 -0
  32. data/test/schema/ids/ibm_db_specific_schema.rb +137 -0
  33. data/test/schema/luw/ibm_db_specific_schema.rb +134 -0
  34. data/test/schema/schema.rb +499 -0
  35. data/test/schema/zOS/ibm_db_specific_schema.rb +205 -0
  36. metadata +115 -0
@@ -0,0 +1,197 @@
1
+ require "cases/helper"
2
+ require 'stringio'
3
+
4
+
5
+ class SchemaDumperTest < ActiveRecord::TestCase
6
+ def standard_dump
7
+ stream = StringIO.new
8
+ ActiveRecord::SchemaDumper.ignore_tables = []
9
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
10
+ stream.string
11
+ end
12
+
13
+ def test_schema_dump
14
+ output = standard_dump
15
+ assert_match %r{create_table "accounts"}, output
16
+ assert_match %r{create_table "authors"}, output
17
+ assert_no_match %r{create_table "schema_migrations"}, output
18
+ end
19
+
20
+ def test_schema_dump_excludes_sqlite_sequence
21
+ output = standard_dump
22
+ assert_no_match %r{create_table "sqlite_sequence"}, output
23
+ end
24
+
25
+ unless current_adapter?(:IBM_DBAdapter) #DB2 is case insensitive
26
+ def test_schema_dump_includes_camelcase_table_name
27
+ output = standard_dump
28
+ assert_match %r{create_table "CamelCase"}, output
29
+ end
30
+ end
31
+
32
+ def assert_line_up(lines, pattern, required = false)
33
+ return assert(true) if lines.empty?
34
+ matches = lines.map { |line| line.match(pattern) }
35
+ assert matches.all? if required
36
+ matches.compact!
37
+ return assert(true) if matches.empty?
38
+ assert_equal 1, matches.map{ |match| match.offset(0).first }.uniq.length
39
+ end
40
+
41
+ def column_definition_lines(output = standard_dump)
42
+ output.scan(/^( *)create_table.*?\n(.*?)^\1end/m).map{ |m| m.last.split(/\n/) }
43
+ end
44
+
45
+ def test_types_line_up
46
+ column_definition_lines.each do |column_set|
47
+ next if column_set.empty?
48
+
49
+ lengths = column_set.map do |column|
50
+ if match = column.match(/t\.(?:integer|decimal|float|datetime|timestamp|time|date|text|binary|string|boolean)\s+"/)
51
+ match[0].length
52
+ end
53
+ end
54
+
55
+ assert_equal 1, lengths.uniq.length
56
+ end
57
+ end
58
+
59
+ def test_arguments_line_up
60
+ column_definition_lines.each do |column_set|
61
+ assert_line_up(column_set, /:default => /)
62
+ assert_line_up(column_set, /:limit => /)
63
+ assert_line_up(column_set, /:null => /)
64
+ end
65
+ end
66
+
67
+ def test_no_dump_errors
68
+ output = standard_dump
69
+ assert_no_match %r{\# Could not dump table}, output
70
+ end
71
+
72
+ def test_schema_dump_includes_not_null_columns
73
+ stream = StringIO.new
74
+
75
+ ActiveRecord::SchemaDumper.ignore_tables = [/^[^r]/]
76
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
77
+ output = stream.string
78
+ assert_match %r{:null => false}, output
79
+ end
80
+
81
+ def test_schema_dump_includes_limit_constraint_for_integer_columns
82
+ stream = StringIO.new
83
+
84
+ ActiveRecord::SchemaDumper.ignore_tables = [/^(?!integer_limits)/]
85
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
86
+ output = stream.string
87
+
88
+ if current_adapter?(:PostgreSQLAdapter)
89
+ assert_match %r{c_int_1.*:limit => 2}, output
90
+ assert_match %r{c_int_2.*:limit => 2}, output
91
+
92
+ # int 3 is 4 bytes in postgresql
93
+ assert_match %r{c_int_3.*}, output
94
+ assert_no_match %r{c_int_3.*:limit}, output
95
+
96
+ assert_match %r{c_int_4.*}, output
97
+ assert_no_match %r{c_int_4.*:limit}, output
98
+ elsif current_adapter?(:MysqlAdapter)
99
+ assert_match %r{c_int_1.*:limit => 1}, output
100
+ assert_match %r{c_int_2.*:limit => 2}, output
101
+ assert_match %r{c_int_3.*:limit => 3}, output
102
+
103
+ assert_match %r{c_int_4.*}, output
104
+ assert_no_match %r{c_int_4.*:limit}, output
105
+ elsif current_adapter?(:SQLiteAdapter)
106
+ assert_match %r{c_int_1.*:limit => 1}, output
107
+ assert_match %r{c_int_2.*:limit => 2}, output
108
+ assert_match %r{c_int_3.*:limit => 3}, output
109
+ assert_match %r{c_int_4.*:limit => 4}, output
110
+ end
111
+ assert_match %r{c_int_without_limit.*}, output
112
+ assert_no_match %r{c_int_without_limit.*:limit}, output
113
+
114
+ if current_adapter?(:SQLiteAdapter)
115
+ assert_match %r{c_int_5.*:limit => 5}, output
116
+ assert_match %r{c_int_6.*:limit => 6}, output
117
+ assert_match %r{c_int_7.*:limit => 7}, output
118
+ assert_match %r{c_int_8.*:limit => 8}, output
119
+ else
120
+ unless current_adapter?(:IBM_DBAdapter) #IBM data servers do not support limits on integer datatype
121
+ assert_match %r{c_int_5.*:limit => 8}, output
122
+ assert_match %r{c_int_6.*:limit => 8}, output
123
+ assert_match %r{c_int_7.*:limit => 8}, output
124
+ assert_match %r{c_int_8.*:limit => 8}, output
125
+ end
126
+ end
127
+ end
128
+
129
+ def test_schema_dump_with_string_ignored_table
130
+ stream = StringIO.new
131
+
132
+ ActiveRecord::SchemaDumper.ignore_tables = ['accounts']
133
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
134
+ output = stream.string
135
+ assert_no_match %r{create_table "accounts"}, output
136
+ assert_match %r{create_table "authors"}, output
137
+ assert_no_match %r{create_table "schema_migrations"}, output
138
+ end
139
+
140
+ def test_schema_dump_with_regexp_ignored_table
141
+ stream = StringIO.new
142
+
143
+ ActiveRecord::SchemaDumper.ignore_tables = [/^account/]
144
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
145
+ output = stream.string
146
+ assert_no_match %r{create_table "accounts"}, output
147
+ assert_match %r{create_table "authors"}, output
148
+ assert_no_match %r{create_table "schema_migrations"}, output
149
+ end
150
+
151
+ def test_schema_dump_illegal_ignored_table_value
152
+ stream = StringIO.new
153
+ ActiveRecord::SchemaDumper.ignore_tables = [5]
154
+ assert_raise(StandardError) do
155
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
156
+ end
157
+ end
158
+
159
+ def test_schema_dumps_index_columns_in_right_order
160
+ index_definition = standard_dump.split(/\n/).grep(/add_index.*companies/).first.strip
161
+ assert_equal 'add_index "companies", ["firm_id", "type", "rating", "ruby_type"], :name => "company_index"', index_definition
162
+ end
163
+
164
+ def test_schema_dump_should_honor_nonstandard_primary_keys
165
+ output = standard_dump
166
+ match = output.match(%r{create_table "movies"(.*)do})
167
+ assert_not_nil(match, "nonstandardpk table not found")
168
+ assert_match %r(:primary_key => "movieid"), match[1], "non-standard primary key not preserved"
169
+ end
170
+
171
+ if current_adapter?(:MysqlAdapter)
172
+ def test_schema_dump_should_not_add_default_value_for_mysql_text_field
173
+ output = standard_dump
174
+ assert_match %r{t.text\s+"body",\s+:null => false$}, output
175
+ end
176
+
177
+ def test_schema_dump_includes_length_for_mysql_blob_and_text_fields
178
+ output = standard_dump
179
+ assert_match %r{t.binary\s+"tiny_blob",\s+:limit => 255$}, output
180
+ assert_match %r{t.binary\s+"normal_blob"$}, output
181
+ assert_match %r{t.binary\s+"medium_blob",\s+:limit => 16777215$}, output
182
+ assert_match %r{t.binary\s+"long_blob",\s+:limit => 2147483647$}, output
183
+ assert_match %r{t.text\s+"tiny_text",\s+:limit => 255$}, output
184
+ assert_match %r{t.text\s+"normal_text"$}, output
185
+ assert_match %r{t.text\s+"medium_text",\s+:limit => 16777215$}, output
186
+ assert_match %r{t.text\s+"long_text",\s+:limit => 2147483647$}, output
187
+ end
188
+ end
189
+
190
+ def test_schema_dump_includes_decimal_options
191
+ stream = StringIO.new
192
+ ActiveRecord::SchemaDumper.ignore_tables = [/^[^n]/]
193
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
194
+ output = stream.string
195
+ assert_match %r{:precision => 3,[[:space:]]+:scale => 2,[[:space:]]+:default => 2.78}, output
196
+ end
197
+ end
@@ -0,0 +1,1604 @@
1
+ # encoding: utf-8
2
+ require "cases/helper"
3
+ require 'mocha'
4
+
5
+ require 'models/topic'
6
+ require 'models/reply'
7
+ require 'models/person'
8
+ require 'models/developer'
9
+ require 'models/warehouse_thing'
10
+ require 'models/guid'
11
+ require 'models/owner'
12
+ require 'models/pet'
13
+ require 'models/event'
14
+
15
+ # The following methods in Topic are used in test_conditional_validation_*
16
+ class Topic
17
+ has_many :unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
18
+ has_many :silly_unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
19
+
20
+ def condition_is_true
21
+ true
22
+ end
23
+
24
+ def condition_is_true_but_its_not
25
+ false
26
+ end
27
+ end
28
+
29
+ class ProtectedPerson < ActiveRecord::Base
30
+ set_table_name 'people'
31
+ attr_accessor :addon
32
+ attr_protected :first_name
33
+ end
34
+
35
+ class UniqueReply < Reply
36
+ validates_uniqueness_of :content, :scope => 'parent_id'
37
+ end
38
+
39
+ class SillyUniqueReply < UniqueReply
40
+ end
41
+
42
+ class Wizard < ActiveRecord::Base
43
+ self.abstract_class = true
44
+
45
+ validates_uniqueness_of :name
46
+ end
47
+
48
+ class IneptWizard < Wizard
49
+ validates_uniqueness_of :city
50
+ end
51
+
52
+ class Conjurer < IneptWizard
53
+ end
54
+
55
+ class Thaumaturgist < IneptWizard
56
+ end
57
+
58
+
59
+ class ValidationsTest < ActiveRecord::TestCase
60
+ fixtures :topics, :developers, :warehouse_things
61
+
62
+ # Most of the tests mess with the validations of Topic, so lets repair it all the time.
63
+ # Other classes we mess with will be dealt with in the specific tests
64
+ repair_validations(Topic)
65
+
66
+ def test_single_field_validation
67
+ r = Reply.new
68
+ r.title = "There's no content!"
69
+ assert !r.valid?, "A reply without content shouldn't be saveable"
70
+
71
+ r.content = "Messa content!"
72
+ assert r.valid?, "A reply with content should be saveable"
73
+ end
74
+
75
+ def test_single_attr_validation_and_error_msg
76
+ r = Reply.new
77
+ r.title = "There's no content!"
78
+ assert !r.valid?
79
+ assert r.errors.invalid?("content"), "A reply without content should mark that attribute as invalid"
80
+ assert_equal "Empty", r.errors.on("content"), "A reply without content should contain an error"
81
+ assert_equal 1, r.errors.count
82
+ end
83
+
84
+ def test_double_attr_validation_and_error_msg
85
+ r = Reply.new
86
+ assert !r.valid?
87
+
88
+ assert r.errors.invalid?("title"), "A reply without title should mark that attribute as invalid"
89
+ assert_equal "Empty", r.errors.on("title"), "A reply without title should contain an error"
90
+
91
+ assert r.errors.invalid?("content"), "A reply without content should mark that attribute as invalid"
92
+ assert_equal "Empty", r.errors.on("content"), "A reply without content should contain an error"
93
+
94
+ assert_equal 2, r.errors.count
95
+ end
96
+
97
+ def test_error_on_create
98
+ r = Reply.new
99
+ r.title = "Wrong Create"
100
+ assert !r.valid?
101
+ assert r.errors.invalid?("title"), "A reply with a bad title should mark that attribute as invalid"
102
+ assert_equal "is Wrong Create", r.errors.on("title"), "A reply with a bad content should contain an error"
103
+ end
104
+
105
+ def test_error_on_update
106
+ r = Reply.new
107
+ r.title = "Bad"
108
+ r.content = "Good"
109
+ assert r.save, "First save should be successful"
110
+
111
+ r.title = "Wrong Update"
112
+ assert !r.save, "Second save should fail"
113
+
114
+ assert r.errors.invalid?("title"), "A reply with a bad title should mark that attribute as invalid"
115
+ assert_equal "is Wrong Update", r.errors.on("title"), "A reply with a bad content should contain an error"
116
+ end
117
+
118
+ def test_invalid_record_exception
119
+ assert_raise(ActiveRecord::RecordInvalid) { Reply.create! }
120
+ assert_raise(ActiveRecord::RecordInvalid) { Reply.new.save! }
121
+
122
+ begin
123
+ r = Reply.new
124
+ r.save!
125
+ flunk
126
+ rescue ActiveRecord::RecordInvalid => invalid
127
+ assert_equal r, invalid.record
128
+ end
129
+ end
130
+
131
+ def test_exception_on_create_bang_many
132
+ assert_raise(ActiveRecord::RecordInvalid) do
133
+ Reply.create!([ { "title" => "OK" }, { "title" => "Wrong Create" }])
134
+ end
135
+ end
136
+
137
+ def test_exception_on_create_bang_with_block
138
+ assert_raise(ActiveRecord::RecordInvalid) do
139
+ Reply.create!({ "title" => "OK" }) do |r|
140
+ r.content = nil
141
+ end
142
+ end
143
+ end
144
+
145
+ def test_exception_on_create_bang_many_with_block
146
+ assert_raise(ActiveRecord::RecordInvalid) do
147
+ Reply.create!([{ "title" => "OK" }, { "title" => "Wrong Create" }]) do |r|
148
+ r.content = nil
149
+ end
150
+ end
151
+ end
152
+
153
+ def test_scoped_create_without_attributes
154
+ Reply.with_scope(:create => {}) do
155
+ assert_raise(ActiveRecord::RecordInvalid) { Reply.create! }
156
+ end
157
+ end
158
+
159
+ def test_create_with_exceptions_using_scope_for_protected_attributes
160
+ assert_nothing_raised do
161
+ ProtectedPerson.with_scope( :create => { :first_name => "Mary" } ) do
162
+ person = ProtectedPerson.create! :addon => "Addon"
163
+ assert_equal person.first_name, "Mary", "scope should ignore attr_protected"
164
+ end
165
+ end
166
+ end
167
+
168
+ def test_create_with_exceptions_using_scope_and_empty_attributes
169
+ assert_nothing_raised do
170
+ ProtectedPerson.with_scope( :create => { :first_name => "Mary" } ) do
171
+ person = ProtectedPerson.create!
172
+ assert_equal person.first_name, "Mary", "should be ok when no attributes are passed to create!"
173
+ end
174
+ end
175
+ end
176
+
177
+ def test_single_error_per_attr_iteration
178
+ r = Reply.new
179
+ r.save
180
+
181
+ errors = []
182
+ r.errors.each { |attr, msg| errors << [attr, msg] }
183
+
184
+ assert errors.include?(["title", "Empty"])
185
+ assert errors.include?(["content", "Empty"])
186
+ end
187
+
188
+ def test_multiple_errors_per_attr_iteration_with_full_error_composition
189
+ r = Reply.new
190
+ r.title = "Wrong Create"
191
+ r.content = "Mismatch"
192
+ r.save
193
+
194
+ errors = []
195
+ r.errors.each_full { |error| errors << error }
196
+
197
+ assert_equal "Title is Wrong Create", errors[0]
198
+ assert_equal "Title is Content Mismatch", errors[1]
199
+ assert_equal 2, r.errors.count
200
+ end
201
+
202
+ def test_errors_on_base
203
+ r = Reply.new
204
+ r.content = "Mismatch"
205
+ r.save
206
+ r.errors.add_to_base "Reply is not dignifying"
207
+
208
+ errors = []
209
+ r.errors.each_full { |error| errors << error }
210
+
211
+ assert_equal "Reply is not dignifying", r.errors.on_base
212
+
213
+ assert errors.include?("Title Empty")
214
+ assert errors.include?("Reply is not dignifying")
215
+ assert_equal 2, r.errors.count
216
+ end
217
+
218
+ def test_create_without_validation
219
+ reply = Reply.new
220
+ assert !reply.save
221
+ assert reply.save(false)
222
+ end
223
+
224
+ def test_create_without_validation_bang
225
+ count = Reply.count
226
+ assert_nothing_raised { Reply.new.save_without_validation! }
227
+ assert count+1, Reply.count
228
+ end
229
+
230
+ def test_validates_each
231
+ hits = 0
232
+ Topic.validates_each(:title, :content, [:title, :content]) do |record, attr|
233
+ record.errors.add attr, 'gotcha'
234
+ hits += 1
235
+ end
236
+ t = Topic.new("title" => "valid", "content" => "whatever")
237
+ assert !t.save
238
+ assert_equal 4, hits
239
+ assert_equal %w(gotcha gotcha), t.errors.on(:title)
240
+ assert_equal %w(gotcha gotcha), t.errors.on(:content)
241
+ end
242
+
243
+ def test_no_title_confirmation
244
+ Topic.validates_confirmation_of(:title)
245
+
246
+ t = Topic.new(:author_name => "Plutarch")
247
+ assert t.valid?
248
+
249
+ t.title_confirmation = "Parallel Lives"
250
+ assert !t.valid?
251
+
252
+ t.title_confirmation = nil
253
+ t.title = "Parallel Lives"
254
+ assert t.valid?
255
+
256
+ t.title_confirmation = "Parallel Lives"
257
+ assert t.valid?
258
+ end
259
+
260
+ def test_title_confirmation
261
+ Topic.validates_confirmation_of(:title)
262
+
263
+ t = Topic.create("title" => "We should be confirmed","title_confirmation" => "")
264
+ assert !t.save
265
+
266
+ t.title_confirmation = "We should be confirmed"
267
+ assert t.save
268
+ end
269
+
270
+ def test_terms_of_service_agreement_no_acceptance
271
+ Topic.validates_acceptance_of(:terms_of_service, :on => :create)
272
+
273
+ t = Topic.create("title" => "We should not be confirmed")
274
+ assert t.save
275
+ end
276
+
277
+ def test_terms_of_service_agreement
278
+ Topic.validates_acceptance_of(:terms_of_service, :on => :create)
279
+
280
+ t = Topic.create("title" => "We should be confirmed","terms_of_service" => "")
281
+ assert !t.save
282
+ assert_equal "must be accepted", t.errors.on(:terms_of_service)
283
+
284
+ t.terms_of_service = "1"
285
+ assert t.save
286
+ end
287
+
288
+
289
+ def test_eula
290
+ Topic.validates_acceptance_of(:eula, :message => "must be abided", :on => :create)
291
+
292
+ t = Topic.create("title" => "We should be confirmed","eula" => "")
293
+ assert !t.save
294
+ assert_equal "must be abided", t.errors.on(:eula)
295
+
296
+ t.eula = "1"
297
+ assert t.save
298
+ end
299
+
300
+ def test_terms_of_service_agreement_with_accept_value
301
+ Topic.validates_acceptance_of(:terms_of_service, :on => :create, :accept => "I agree.")
302
+
303
+ t = Topic.create("title" => "We should be confirmed", "terms_of_service" => "")
304
+ assert !t.save
305
+ assert_equal "must be accepted", t.errors.on(:terms_of_service)
306
+
307
+ t.terms_of_service = "I agree."
308
+ assert t.save
309
+ end
310
+
311
+ def test_validates_acceptance_of_as_database_column
312
+ repair_validations(Reply) do
313
+ Reply.validates_acceptance_of(:author_name)
314
+
315
+ reply = Reply.create("author_name" => "Dan Brown")
316
+ assert_equal "Dan Brown", reply["author_name"]
317
+ end
318
+ end
319
+
320
+ def test_validates_acceptance_of_with_non_existant_table
321
+ Object.const_set :IncorporealModel, Class.new(ActiveRecord::Base)
322
+
323
+ assert_nothing_raised ActiveRecord::StatementInvalid do
324
+ IncorporealModel.validates_acceptance_of(:incorporeal_column)
325
+ end
326
+ end
327
+
328
+ def test_validate_presences
329
+ Topic.validates_presence_of(:title, :content)
330
+
331
+ t = Topic.create
332
+ assert !t.save
333
+ assert_equal "can't be blank", t.errors.on(:title)
334
+ assert_equal "can't be blank", t.errors.on(:content)
335
+
336
+ t.title = "something"
337
+ t.content = " "
338
+
339
+ assert !t.save
340
+ assert_equal "can't be blank", t.errors.on(:content)
341
+
342
+ t.content = "like stuff"
343
+
344
+ assert t.save
345
+ end
346
+
347
+ def test_validate_uniqueness
348
+ Topic.validates_uniqueness_of(:title)
349
+
350
+ t = Topic.new("title" => "I'm unique!")
351
+ assert t.save, "Should save t as unique"
352
+
353
+ t.content = "Remaining unique"
354
+ assert t.save, "Should still save t as unique"
355
+
356
+ t2 = Topic.new("title" => "I'm unique!")
357
+ assert !t2.valid?, "Shouldn't be valid"
358
+ assert !t2.save, "Shouldn't save t2 as unique"
359
+ assert_equal "has already been taken", t2.errors.on(:title)
360
+
361
+ t2.title = "Now Im really also unique"
362
+ assert t2.save, "Should now save t2 as unique"
363
+ end
364
+
365
+ def test_validates_uniquness_with_newline_chars
366
+ Topic.validates_uniqueness_of(:title, :case_sensitive => false)
367
+
368
+ t = Topic.new("title" => "new\nline")
369
+ assert t.save, "Should save t as unique"
370
+ end
371
+
372
+ def test_validate_uniqueness_with_scope
373
+ repair_validations(Reply) do
374
+ Reply.validates_uniqueness_of(:content, :scope => "parent_id")
375
+
376
+ t = Topic.create("title" => "I'm unique!")
377
+
378
+ r1 = t.replies.create "title" => "r1", "content" => "hello world"
379
+ assert r1.valid?, "Saving r1"
380
+
381
+ r2 = t.replies.create "title" => "r2", "content" => "hello world"
382
+ assert !r2.valid?, "Saving r2 first time"
383
+
384
+ r2.content = "something else"
385
+ assert r2.save, "Saving r2 second time"
386
+
387
+ t2 = Topic.create("title" => "I'm unique too!")
388
+ r3 = t2.replies.create "title" => "r3", "content" => "hello world"
389
+ assert r3.valid?, "Saving r3"
390
+ end
391
+ end
392
+
393
+ def test_validate_uniqueness_scoped_to_defining_class
394
+ t = Topic.create("title" => "What, me worry?")
395
+
396
+ r1 = t.unique_replies.create "title" => "r1", "content" => "a barrel of fun"
397
+ assert r1.valid?, "Saving r1"
398
+
399
+ r2 = t.silly_unique_replies.create "title" => "r2", "content" => "a barrel of fun"
400
+ assert !r2.valid?, "Saving r2"
401
+
402
+ # Should succeed as validates_uniqueness_of only applies to
403
+ # UniqueReply and its subclasses
404
+ r3 = t.replies.create "title" => "r2", "content" => "a barrel of fun"
405
+ assert r3.valid?, "Saving r3"
406
+ end
407
+
408
+ def test_validate_uniqueness_with_scope_array
409
+ repair_validations(Reply) do
410
+ Reply.validates_uniqueness_of(:author_name, :scope => [:author_email_address, :parent_id])
411
+
412
+ t = Topic.create("title" => "The earth is actually flat!")
413
+
414
+ r1 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply"
415
+ assert r1.valid?, "Saving r1"
416
+
417
+ r2 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply again..."
418
+ assert !r2.valid?, "Saving r2. Double reply by same author."
419
+
420
+ r2.author_email_address = "jeremy_alt_email@rubyonrails.com"
421
+ assert r2.save, "Saving r2 the second time."
422
+
423
+ r3 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy_alt_email@rubyonrails.com", "title" => "You're wrong", "content" => "It's cubic"
424
+ assert !r3.valid?, "Saving r3"
425
+
426
+ r3.author_name = "jj"
427
+ assert r3.save, "Saving r3 the second time."
428
+
429
+ r3.author_name = "jeremy"
430
+ assert !r3.save, "Saving r3 the third time."
431
+ end
432
+ end
433
+
434
+ def test_validate_case_insensitive_uniqueness
435
+ Topic.validates_uniqueness_of(:title, :parent_id, :case_sensitive => false, :allow_nil => true)
436
+
437
+ t = Topic.new("title" => "I'm unique!", :parent_id => 2)
438
+ assert t.save, "Should save t as unique"
439
+
440
+ t.content = "Remaining unique"
441
+ assert t.save, "Should still save t as unique"
442
+
443
+ t2 = Topic.new("title" => "I'm UNIQUE!", :parent_id => 1)
444
+ assert !t2.valid?, "Shouldn't be valid"
445
+ assert !t2.save, "Shouldn't save t2 as unique"
446
+ assert t2.errors.on(:title)
447
+ assert t2.errors.on(:parent_id)
448
+ assert_equal "has already been taken", t2.errors.on(:title)
449
+
450
+ t2.title = "I'm truly UNIQUE!"
451
+ assert !t2.valid?, "Shouldn't be valid"
452
+ assert !t2.save, "Shouldn't save t2 as unique"
453
+ assert_nil t2.errors.on(:title)
454
+ assert t2.errors.on(:parent_id)
455
+
456
+ t2.parent_id = 4
457
+ assert t2.save, "Should now save t2 as unique"
458
+
459
+ t2.parent_id = nil
460
+ t2.title = nil
461
+ assert t2.valid?, "should validate with nil"
462
+ assert t2.save, "should save with nil"
463
+
464
+ with_kcode('UTF8') do
465
+ t_utf8 = Topic.new("title" => "Я тоже уникальный!")
466
+ assert t_utf8.save, "Should save t_utf8 as unique"
467
+
468
+ # If database hasn't UTF-8 character set, this test fails
469
+ if Topic.find(t_utf8, :select => 'LOWER(title) AS title').title == "я тоже уникальный!"
470
+ t2_utf8 = Topic.new("title" => "я тоже УНИКАЛЬНЫЙ!")
471
+ assert !t2_utf8.valid?, "Shouldn't be valid"
472
+ assert !t2_utf8.save, "Shouldn't save t2_utf8 as unique"
473
+ end
474
+ end
475
+ end
476
+
477
+ def test_validate_case_sensitive_uniqueness
478
+ Topic.validates_uniqueness_of(:title, :case_sensitive => true, :allow_nil => true)
479
+
480
+ t = Topic.new("title" => "I'm unique!")
481
+ assert t.save, "Should save t as unique"
482
+
483
+ t.content = "Remaining unique"
484
+ assert t.save, "Should still save t as unique"
485
+
486
+ t2 = Topic.new("title" => "I'M UNIQUE!")
487
+ assert t2.valid?, "Should be valid"
488
+ assert t2.save, "Should save t2 as unique"
489
+ assert !t2.errors.on(:title)
490
+ assert !t2.errors.on(:parent_id)
491
+ assert_not_equal "has already been taken", t2.errors.on(:title)
492
+
493
+ t3 = Topic.new("title" => "I'M uNiQUe!")
494
+ assert t3.valid?, "Should be valid"
495
+ assert t3.save, "Should save t2 as unique"
496
+ assert !t3.errors.on(:title)
497
+ assert !t3.errors.on(:parent_id)
498
+ assert_not_equal "has already been taken", t3.errors.on(:title)
499
+ end
500
+
501
+ def test_validate_case_sensitive_uniqueness_with_attribute_passed_as_integer
502
+ Topic.validates_uniqueness_of(:title, :case_sensitve => true)
503
+ t = Topic.create!('title' => 101)
504
+
505
+ t2 = Topic.new('title' => 101)
506
+ assert !t2.valid?
507
+ assert t2.errors.on(:title)
508
+ end
509
+
510
+ def test_validate_uniqueness_with_non_standard_table_names
511
+ i1 = WarehouseThing.create(:value => 1000)
512
+ assert !i1.valid?, "i1 should not be valid"
513
+ assert i1.errors.on(:value), "Should not be empty"
514
+ end
515
+
516
+ def test_validates_uniqueness_inside_with_scope
517
+ Topic.validates_uniqueness_of(:title)
518
+
519
+ Topic.with_scope(:find => { :conditions => { :author_name => "David" } }) do
520
+ t1 = Topic.new("title" => "I'm unique!", "author_name" => "Mary")
521
+ assert t1.save
522
+ t2 = Topic.new("title" => "I'm unique!", "author_name" => "David")
523
+ assert !t2.valid?
524
+ end
525
+ end
526
+
527
+ def test_validate_uniqueness_with_columns_which_are_sql_keywords
528
+ repair_validations(Guid) do
529
+ Guid.validates_uniqueness_of :key
530
+ g = Guid.new
531
+ g.key = "foo"
532
+ assert_nothing_raised { !g.valid? }
533
+ end
534
+ end
535
+
536
+ def test_validate_uniqueness_with_limit
537
+ # Event.title is limited to 5 characters
538
+ e1 = Event.create(:title => "abcde")
539
+ assert e1.valid?, "Could not create an event with a unique, 5 character title"
540
+ e2 = Event.create(:title => "abcdefgh")
541
+ assert !e2.valid?, "Created an event whose title, with limit taken into account, is not unique"
542
+ end
543
+
544
+ def test_validate_straight_inheritance_uniqueness
545
+ w1 = IneptWizard.create(:name => "Rincewind", :city => "Ankh-Morpork")
546
+ assert w1.valid?, "Saving w1"
547
+
548
+ # Should use validation from base class (which is abstract)
549
+ w2 = IneptWizard.new(:name => "Rincewind", :city => "Quirm")
550
+ assert !w2.valid?, "w2 shouldn't be valid"
551
+ assert w2.errors.on(:name), "Should have errors for name"
552
+ assert_equal "has already been taken", w2.errors.on(:name), "Should have uniqueness message for name"
553
+
554
+ w3 = Conjurer.new(:name => "Rincewind", :city => "Quirm")
555
+ assert !w3.valid?, "w3 shouldn't be valid"
556
+ assert w3.errors.on(:name), "Should have errors for name"
557
+ assert_equal "has already been taken", w3.errors.on(:name), "Should have uniqueness message for name"
558
+
559
+ w4 = Conjurer.create(:name => "The Amazing Bonko", :city => "Quirm")
560
+ assert w4.valid?, "Saving w4"
561
+
562
+ w5 = Thaumaturgist.new(:name => "The Amazing Bonko", :city => "Lancre")
563
+ assert !w5.valid?, "w5 shouldn't be valid"
564
+ assert w5.errors.on(:name), "Should have errors for name"
565
+ assert_equal "has already been taken", w5.errors.on(:name), "Should have uniqueness message for name"
566
+
567
+ w6 = Thaumaturgist.new(:name => "Mustrum Ridcully", :city => "Quirm")
568
+ assert !w6.valid?, "w6 shouldn't be valid"
569
+ assert w6.errors.on(:city), "Should have errors for city"
570
+ assert_equal "has already been taken", w6.errors.on(:city), "Should have uniqueness message for city"
571
+ end
572
+
573
+ def test_validate_format
574
+ Topic.validates_format_of(:title, :content, :with => /^Validation\smacros \w+!$/, :message => "is bad data")
575
+
576
+ t = Topic.create("title" => "i'm incorrect", "content" => "Validation macros rule!")
577
+ assert !t.valid?, "Shouldn't be valid"
578
+ assert !t.save, "Shouldn't save because it's invalid"
579
+ assert_equal "is bad data", t.errors.on(:title)
580
+ assert_nil t.errors.on(:content)
581
+
582
+ t.title = "Validation macros rule!"
583
+
584
+ assert t.save
585
+ assert_nil t.errors.on(:title)
586
+
587
+ assert_raise(ArgumentError) { Topic.validates_format_of(:title, :content) }
588
+ end
589
+
590
+ def test_validate_format_with_allow_blank
591
+ Topic.validates_format_of(:title, :with => /^Validation\smacros \w+!$/, :allow_blank=>true)
592
+ assert !Topic.create("title" => "Shouldn't be valid").valid?
593
+ assert Topic.create("title" => "").valid?
594
+ assert Topic.create("title" => nil).valid?
595
+ assert Topic.create("title" => "Validation macros rule!").valid?
596
+ end
597
+
598
+ # testing ticket #3142
599
+ def test_validate_format_numeric
600
+ Topic.validates_format_of(:title, :content, :with => /^[1-9][0-9]*$/, :message => "is bad data")
601
+
602
+ t = Topic.create("title" => "72x", "content" => "6789")
603
+ assert !t.valid?, "Shouldn't be valid"
604
+ assert !t.save, "Shouldn't save because it's invalid"
605
+ assert_equal "is bad data", t.errors.on(:title)
606
+ assert_nil t.errors.on(:content)
607
+
608
+ t.title = "-11"
609
+ assert !t.valid?, "Shouldn't be valid"
610
+
611
+ t.title = "03"
612
+ assert !t.valid?, "Shouldn't be valid"
613
+
614
+ t.title = "z44"
615
+ assert !t.valid?, "Shouldn't be valid"
616
+
617
+ t.title = "5v7"
618
+ assert !t.valid?, "Shouldn't be valid"
619
+
620
+ t.title = "1"
621
+
622
+ assert t.save
623
+ assert_nil t.errors.on(:title)
624
+ end
625
+
626
+ def test_validate_format_with_formatted_message
627
+ Topic.validates_format_of(:title, :with => /^Valid Title$/, :message => "can't be {{value}}")
628
+ t = Topic.create(:title => 'Invalid title')
629
+ assert_equal "can't be Invalid title", t.errors.on(:title)
630
+ end
631
+
632
+ def test_validates_inclusion_of
633
+ Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ) )
634
+
635
+ assert !Topic.create("title" => "a!", "content" => "abc").valid?
636
+ assert !Topic.create("title" => "a b", "content" => "abc").valid?
637
+ assert !Topic.create("title" => nil, "content" => "def").valid?
638
+
639
+ t = Topic.create("title" => "a", "content" => "I know you are but what am I?")
640
+ assert t.valid?
641
+ t.title = "uhoh"
642
+ assert !t.valid?
643
+ assert t.errors.on(:title)
644
+ assert_equal "is not included in the list", t.errors["title"]
645
+
646
+ assert_raise(ArgumentError) { Topic.validates_inclusion_of( :title, :in => nil ) }
647
+ assert_raise(ArgumentError) { Topic.validates_inclusion_of( :title, :in => 0) }
648
+
649
+ assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => "hi!" ) }
650
+ assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => {} ) }
651
+ assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => [] ) }
652
+ end
653
+
654
+ def test_validates_inclusion_of_with_allow_nil
655
+ Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :allow_nil=>true )
656
+
657
+ assert !Topic.create("title" => "a!", "content" => "abc").valid?
658
+ assert !Topic.create("title" => "", "content" => "abc").valid?
659
+ assert Topic.create("title" => nil, "content" => "abc").valid?
660
+ end
661
+
662
+ def test_numericality_with_getter_method
663
+ repair_validations(Developer) do
664
+ Developer.validates_numericality_of( :salary )
665
+ developer = Developer.new("name" => "michael", "salary" => nil)
666
+ developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
667
+ assert developer.valid?
668
+ end
669
+ end
670
+
671
+ def test_validates_length_of_with_allow_nil
672
+ Topic.validates_length_of( :title, :is => 5, :allow_nil=>true )
673
+
674
+ assert !Topic.create("title" => "ab").valid?
675
+ assert !Topic.create("title" => "").valid?
676
+ assert Topic.create("title" => nil).valid?
677
+ assert Topic.create("title" => "abcde").valid?
678
+ end
679
+
680
+ def test_validates_length_of_with_allow_blank
681
+ Topic.validates_length_of( :title, :is => 5, :allow_blank=>true )
682
+
683
+ assert !Topic.create("title" => "ab").valid?
684
+ assert Topic.create("title" => "").valid?
685
+ assert Topic.create("title" => nil).valid?
686
+ assert Topic.create("title" => "abcde").valid?
687
+ end
688
+
689
+ def test_validates_inclusion_of_with_formatted_message
690
+ Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :message => "option {{value}} is not in the list" )
691
+
692
+ assert Topic.create("title" => "a", "content" => "abc").valid?
693
+
694
+ t = Topic.create("title" => "uhoh", "content" => "abc")
695
+ assert !t.valid?
696
+ assert t.errors.on(:title)
697
+ assert_equal "option uhoh is not in the list", t.errors["title"]
698
+ end
699
+
700
+ def test_numericality_with_allow_nil_and_getter_method
701
+ repair_validations(Developer) do
702
+ Developer.validates_numericality_of( :salary, :allow_nil => true)
703
+ developer = Developer.new("name" => "michael", "salary" => nil)
704
+ developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
705
+ assert developer.valid?
706
+ end
707
+ end
708
+
709
+ def test_validates_exclusion_of
710
+ Topic.validates_exclusion_of( :title, :in => %w( abe monkey ) )
711
+
712
+ assert Topic.create("title" => "something", "content" => "abc").valid?
713
+ assert !Topic.create("title" => "monkey", "content" => "abc").valid?
714
+ end
715
+
716
+ def test_validates_exclusion_of_with_formatted_message
717
+ Topic.validates_exclusion_of( :title, :in => %w( abe monkey ), :message => "option {{value}} is restricted" )
718
+
719
+ assert Topic.create("title" => "something", "content" => "abc")
720
+
721
+ t = Topic.create("title" => "monkey")
722
+ assert !t.valid?
723
+ assert t.errors.on(:title)
724
+ assert_equal "option monkey is restricted", t.errors["title"]
725
+ end
726
+
727
+ def test_validates_length_of_using_minimum
728
+ Topic.validates_length_of :title, :minimum => 5
729
+
730
+ t = Topic.create("title" => "valid", "content" => "whatever")
731
+ assert t.valid?
732
+
733
+ t.title = "not"
734
+ assert !t.valid?
735
+ assert t.errors.on(:title)
736
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
737
+
738
+ t.title = ""
739
+ assert !t.valid?
740
+ assert t.errors.on(:title)
741
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
742
+
743
+ t.title = nil
744
+ assert !t.valid?
745
+ assert t.errors.on(:title)
746
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
747
+ end
748
+
749
+ def test_optionally_validates_length_of_using_minimum
750
+ Topic.validates_length_of :title, :minimum => 5, :allow_nil => true
751
+
752
+ t = Topic.create("title" => "valid", "content" => "whatever")
753
+ assert t.valid?
754
+
755
+ t.title = nil
756
+ assert t.valid?
757
+ end
758
+
759
+ def test_validates_length_of_using_maximum
760
+ Topic.validates_length_of :title, :maximum => 5
761
+
762
+ t = Topic.create("title" => "valid", "content" => "whatever")
763
+ assert t.valid?
764
+
765
+ t.title = "notvalid"
766
+ assert !t.valid?
767
+ assert t.errors.on(:title)
768
+ assert_equal "is too long (maximum is 5 characters)", t.errors["title"]
769
+
770
+ t.title = ""
771
+ assert t.valid?
772
+
773
+ t.title = nil
774
+ assert !t.valid?
775
+ end
776
+
777
+ def test_optionally_validates_length_of_using_maximum
778
+ Topic.validates_length_of :title, :maximum => 5, :allow_nil => true
779
+
780
+ t = Topic.create("title" => "valid", "content" => "whatever")
781
+ assert t.valid?
782
+
783
+ t.title = nil
784
+ assert t.valid?
785
+ end
786
+
787
+ def test_validates_length_of_using_within
788
+ Topic.validates_length_of(:title, :content, :within => 3..5)
789
+
790
+ t = Topic.new("title" => "a!", "content" => "I'm ooooooooh so very long")
791
+ assert !t.valid?
792
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
793
+ assert_equal "is too long (maximum is 5 characters)", t.errors.on(:content)
794
+
795
+ t.title = nil
796
+ t.content = nil
797
+ assert !t.valid?
798
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
799
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:content)
800
+
801
+ t.title = "abe"
802
+ t.content = "mad"
803
+ assert t.valid?
804
+ end
805
+
806
+ def test_optionally_validates_length_of_using_within
807
+ Topic.validates_length_of :title, :content, :within => 3..5, :allow_nil => true
808
+
809
+ t = Topic.create('title' => 'abc', 'content' => 'abcd')
810
+ assert t.valid?
811
+
812
+ t.title = nil
813
+ assert t.valid?
814
+ end
815
+
816
+ def test_optionally_validates_length_of_using_within_on_create
817
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :create, :too_long => "my string is too long: {{count}}"
818
+
819
+ t = Topic.create("title" => "thisisnotvalid", "content" => "whatever")
820
+ assert !t.save
821
+ assert t.errors.on(:title)
822
+ assert_equal "my string is too long: 10", t.errors[:title]
823
+
824
+ t.title = "butthisis"
825
+ assert t.save
826
+
827
+ t.title = "few"
828
+ assert t.save
829
+
830
+ t.content = "andthisislong"
831
+ assert t.save
832
+
833
+ t.content = t.title = "iamfine"
834
+ assert t.save
835
+ end
836
+
837
+ def test_optionally_validates_length_of_using_within_on_update
838
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :update, :too_short => "my string is too short: {{count}}"
839
+
840
+ t = Topic.create("title" => "vali", "content" => "whatever")
841
+ assert !t.save
842
+ assert t.errors.on(:title)
843
+
844
+ t.title = "not"
845
+ assert !t.save
846
+ assert t.errors.on(:title)
847
+ assert_equal "my string is too short: 5", t.errors[:title]
848
+
849
+ t.title = "valid"
850
+ t.content = "andthisistoolong"
851
+ assert !t.save
852
+ assert t.errors.on(:content)
853
+
854
+ t.content = "iamfine"
855
+ assert t.save
856
+ end
857
+
858
+ def test_validates_length_of_using_is
859
+ Topic.validates_length_of :title, :is => 5
860
+
861
+ t = Topic.create("title" => "valid", "content" => "whatever")
862
+ assert t.valid?
863
+
864
+ t.title = "notvalid"
865
+ assert !t.valid?
866
+ assert t.errors.on(:title)
867
+ assert_equal "is the wrong length (should be 5 characters)", t.errors["title"]
868
+
869
+ t.title = ""
870
+ assert !t.valid?
871
+
872
+ t.title = nil
873
+ assert !t.valid?
874
+ end
875
+
876
+ def test_optionally_validates_length_of_using_is
877
+ Topic.validates_length_of :title, :is => 5, :allow_nil => true
878
+
879
+ t = Topic.create("title" => "valid", "content" => "whatever")
880
+ assert t.valid?
881
+
882
+ t.title = nil
883
+ assert t.valid?
884
+ end
885
+
886
+ def test_validates_length_of_using_bignum
887
+ bigmin = 2 ** 30
888
+ bigmax = 2 ** 32
889
+ bigrange = bigmin...bigmax
890
+ assert_nothing_raised do
891
+ Topic.validates_length_of :title, :is => bigmin + 5
892
+ Topic.validates_length_of :title, :within => bigrange
893
+ Topic.validates_length_of :title, :in => bigrange
894
+ Topic.validates_length_of :title, :minimum => bigmin
895
+ Topic.validates_length_of :title, :maximum => bigmax
896
+ end
897
+ end
898
+
899
+ def test_validates_length_with_globally_modified_error_message
900
+ ActiveSupport::Deprecation.silence do
901
+ ActiveRecord::Errors.default_error_messages[:too_short] = 'tu est trops petit hombre {{count}}'
902
+ end
903
+ Topic.validates_length_of :title, :minimum => 10
904
+ t = Topic.create(:title => 'too short')
905
+ assert !t.valid?
906
+
907
+ assert_equal 'tu est trops petit hombre 10', t.errors['title']
908
+ end
909
+
910
+ def test_validates_size_of_association
911
+ repair_validations(Owner) do
912
+ assert_nothing_raised { Owner.validates_size_of :pets, :minimum => 1 }
913
+ o = Owner.new('name' => 'nopets')
914
+ assert !o.save
915
+ assert o.errors.on(:pets)
916
+ pet = o.pets.build('name' => 'apet')
917
+ assert o.valid?
918
+ end
919
+ end
920
+
921
+ def test_validates_size_of_association_using_within
922
+ repair_validations(Owner) do
923
+ assert_nothing_raised { Owner.validates_size_of :pets, :within => 1..2 }
924
+ o = Owner.new('name' => 'nopets')
925
+ assert !o.save
926
+ assert o.errors.on(:pets)
927
+
928
+ pet = o.pets.build('name' => 'apet')
929
+ assert o.valid?
930
+
931
+ 2.times { o.pets.build('name' => 'apet') }
932
+ assert !o.save
933
+ assert o.errors.on(:pets)
934
+ end
935
+ end
936
+
937
+ def test_validates_length_of_nasty_params
938
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :minimum=>6, :maximum=>9) }
939
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :maximum=>9) }
940
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :minimum=>9) }
941
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :is=>9) }
942
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :minimum=>"a") }
943
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :maximum=>"a") }
944
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>"a") }
945
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :is=>"a") }
946
+ end
947
+
948
+ def test_validates_length_of_custom_errors_for_minimum_with_message
949
+ Topic.validates_length_of( :title, :minimum=>5, :message=>"boo {{count}}" )
950
+ t = Topic.create("title" => "uhoh", "content" => "whatever")
951
+ assert !t.valid?
952
+ assert t.errors.on(:title)
953
+ assert_equal "boo 5", t.errors["title"]
954
+ end
955
+
956
+ def test_validates_length_of_custom_errors_for_minimum_with_too_short
957
+ Topic.validates_length_of( :title, :minimum=>5, :too_short=>"hoo {{count}}" )
958
+ t = Topic.create("title" => "uhoh", "content" => "whatever")
959
+ assert !t.valid?
960
+ assert t.errors.on(:title)
961
+ assert_equal "hoo 5", t.errors["title"]
962
+ end
963
+
964
+ def test_validates_length_of_custom_errors_for_maximum_with_message
965
+ Topic.validates_length_of( :title, :maximum=>5, :message=>"boo {{count}}" )
966
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
967
+ assert !t.valid?
968
+ assert t.errors.on(:title)
969
+ assert_equal "boo 5", t.errors["title"]
970
+ end
971
+
972
+ def test_validates_length_of_custom_errors_for_in
973
+ Topic.validates_length_of(:title, :in => 10..20, :message => "hoo {{count}}")
974
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
975
+ assert !t.valid?
976
+ assert t.errors.on(:title)
977
+ assert_equal "hoo 10", t.errors["title"]
978
+
979
+ t = Topic.create("title" => "uhohuhohuhohuhohuhohuhohuhohuhoh", "content" => "whatever")
980
+ assert !t.valid?
981
+ assert t.errors.on(:title)
982
+ assert_equal "hoo 20", t.errors["title"]
983
+ end
984
+
985
+ def test_validates_length_of_custom_errors_for_maximum_with_too_long
986
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}" )
987
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
988
+ assert !t.valid?
989
+ assert t.errors.on(:title)
990
+ assert_equal "hoo 5", t.errors["title"]
991
+ end
992
+
993
+ def test_validates_length_of_custom_errors_for_is_with_message
994
+ Topic.validates_length_of( :title, :is=>5, :message=>"boo {{count}}" )
995
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
996
+ assert !t.valid?
997
+ assert t.errors.on(:title)
998
+ assert_equal "boo 5", t.errors["title"]
999
+ end
1000
+
1001
+ def test_validates_length_of_custom_errors_for_is_with_wrong_length
1002
+ Topic.validates_length_of( :title, :is=>5, :wrong_length=>"hoo {{count}}" )
1003
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1004
+ assert !t.valid?
1005
+ assert t.errors.on(:title)
1006
+ assert_equal "hoo 5", t.errors["title"]
1007
+ end
1008
+
1009
+ def test_validates_length_of_using_minimum_utf8
1010
+ with_kcode('UTF8') do
1011
+ Topic.validates_length_of :title, :minimum => 5
1012
+
1013
+ t = Topic.create("title" => "一二三四五", "content" => "whatever")
1014
+ assert t.valid?
1015
+
1016
+ t.title = "一二三四"
1017
+ assert !t.valid?
1018
+ assert t.errors.on(:title)
1019
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
1020
+ end
1021
+ end
1022
+
1023
+ def test_validates_length_of_using_maximum_utf8
1024
+ with_kcode('UTF8') do
1025
+ Topic.validates_length_of :title, :maximum => 5
1026
+
1027
+ t = Topic.create("title" => "一二三四五", "content" => "whatever")
1028
+ assert t.valid?
1029
+
1030
+ t.title = "一二34五六"
1031
+ assert !t.valid?
1032
+ assert t.errors.on(:title)
1033
+ assert_equal "is too long (maximum is 5 characters)", t.errors["title"]
1034
+ end
1035
+ end
1036
+
1037
+ def test_validates_length_of_using_within_utf8
1038
+ with_kcode('UTF8') do
1039
+ Topic.validates_length_of(:title, :content, :within => 3..5)
1040
+
1041
+ t = Topic.new("title" => "一二", "content" => "12三四五六七")
1042
+ assert !t.valid?
1043
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
1044
+ assert_equal "is too long (maximum is 5 characters)", t.errors.on(:content)
1045
+ t.title = "一二三"
1046
+ t.content = "12三"
1047
+ assert t.valid?
1048
+ end
1049
+ end
1050
+
1051
+ def test_optionally_validates_length_of_using_within_utf8
1052
+ with_kcode('UTF8') do
1053
+ Topic.validates_length_of :title, :within => 3..5, :allow_nil => true
1054
+
1055
+ t = Topic.create(:title => "一二三四五")
1056
+ assert t.valid?, t.errors.inspect
1057
+
1058
+ t = Topic.create(:title => "一二三")
1059
+ assert t.valid?, t.errors.inspect
1060
+
1061
+ t.title = nil
1062
+ assert t.valid?, t.errors.inspect
1063
+ end
1064
+ end
1065
+
1066
+ def test_optionally_validates_length_of_using_within_on_create_utf8
1067
+ with_kcode('UTF8') do
1068
+ Topic.validates_length_of :title, :within => 5..10, :on => :create, :too_long => "長すぎます: {{count}}"
1069
+
1070
+ t = Topic.create("title" => "一二三四五六七八九十A", "content" => "whatever")
1071
+ assert !t.save
1072
+ assert t.errors.on(:title)
1073
+ assert_equal "長すぎます: 10", t.errors[:title]
1074
+
1075
+ t.title = "一二三四五六七八九"
1076
+ assert t.save
1077
+
1078
+ t.title = "一二3"
1079
+ assert t.save
1080
+
1081
+ t.content = "一二三四五六七八九十"
1082
+ assert t.save
1083
+
1084
+ t.content = t.title = "一二三四五六"
1085
+ assert t.save
1086
+ end
1087
+ end
1088
+
1089
+ def test_optionally_validates_length_of_using_within_on_update_utf8
1090
+ with_kcode('UTF8') do
1091
+ Topic.validates_length_of :title, :within => 5..10, :on => :update, :too_short => "短すぎます: {{count}}"
1092
+
1093
+ t = Topic.create("title" => "一二三4", "content" => "whatever")
1094
+ assert !t.save
1095
+ assert t.errors.on(:title)
1096
+
1097
+ t.title = "1二三4"
1098
+ assert !t.save
1099
+ assert t.errors.on(:title)
1100
+ assert_equal "短すぎます: 5", t.errors[:title]
1101
+
1102
+ t.title = "一二三四五六七八九十A"
1103
+ assert !t.save
1104
+ assert t.errors.on(:title)
1105
+
1106
+ t.title = "一二345"
1107
+ assert t.save
1108
+ end
1109
+ end
1110
+
1111
+ def test_validates_length_of_using_is_utf8
1112
+ with_kcode('UTF8') do
1113
+ Topic.validates_length_of :title, :is => 5
1114
+
1115
+ t = Topic.create("title" => "一二345", "content" => "whatever")
1116
+ assert t.valid?
1117
+
1118
+ t.title = "一二345六"
1119
+ assert !t.valid?
1120
+ assert t.errors.on(:title)
1121
+ assert_equal "is the wrong length (should be 5 characters)", t.errors["title"]
1122
+ end
1123
+ end
1124
+
1125
+ def test_validates_length_of_with_block
1126
+ Topic.validates_length_of :content, :minimum => 5, :too_short=>"Your essay must be at least {{count}} words.",
1127
+ :tokenizer => lambda {|str| str.scan(/\w+/) }
1128
+ t = Topic.create!(:content => "this content should be long enough")
1129
+ assert t.valid?
1130
+
1131
+ t.content = "not long enough"
1132
+ assert !t.valid?
1133
+ assert t.errors.on(:content)
1134
+ assert_equal "Your essay must be at least 5 words.", t.errors[:content]
1135
+ end
1136
+
1137
+ def test_validates_size_of_association_utf8
1138
+ repair_validations(Owner) do
1139
+ with_kcode('UTF8') do
1140
+ assert_nothing_raised { Owner.validates_size_of :pets, :minimum => 1 }
1141
+ o = Owner.new('name' => 'あいうえおかきくけこ')
1142
+ assert !o.save
1143
+ assert o.errors.on(:pets)
1144
+ o.pets.build('name' => 'あいうえおかきくけこ')
1145
+ assert o.valid?
1146
+ end
1147
+ end
1148
+ end
1149
+
1150
+ def test_validates_associated_many
1151
+ Topic.validates_associated( :replies )
1152
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1153
+ t.replies << [r = Reply.new("title" => "A reply"), r2 = Reply.new("title" => "Another reply", "content" => "non-empty"), r3 = Reply.new("title" => "Yet another reply"), r4 = Reply.new("title" => "The last reply", "content" => "non-empty")]
1154
+ assert !t.valid?
1155
+ assert t.errors.on(:replies)
1156
+ assert_equal 1, r.errors.count # make sure all associated objects have been validated
1157
+ assert_equal 0, r2.errors.count
1158
+ assert_equal 1, r3.errors.count
1159
+ assert_equal 0, r4.errors.count
1160
+ r.content = r3.content = "non-empty"
1161
+ assert t.valid?
1162
+ end
1163
+
1164
+ def test_validates_associated_one
1165
+ repair_validations(Reply) do
1166
+ Reply.validates_associated( :topic )
1167
+ Topic.validates_presence_of( :content )
1168
+ r = Reply.new("title" => "A reply", "content" => "with content!")
1169
+ r.topic = Topic.create("title" => "uhohuhoh")
1170
+ assert !r.valid?
1171
+ assert r.errors.on(:topic)
1172
+ r.topic.content = "non-empty"
1173
+ assert r.valid?
1174
+ end
1175
+ end
1176
+
1177
+ def test_validate_block
1178
+ Topic.validate { |topic| topic.errors.add("title", "will never be valid") }
1179
+ t = Topic.create("title" => "Title", "content" => "whatever")
1180
+ assert !t.valid?
1181
+ assert t.errors.on(:title)
1182
+ assert_equal "will never be valid", t.errors["title"]
1183
+ end
1184
+
1185
+ def test_invalid_validator
1186
+ Topic.validate 3
1187
+ assert_raise(ArgumentError) { t = Topic.create }
1188
+ end
1189
+
1190
+ def test_throw_away_typing
1191
+ d = Developer.new("name" => "David", "salary" => "100,000")
1192
+ assert !d.valid?
1193
+ assert_equal 100, d.salary
1194
+ assert_equal "100,000", d.salary_before_type_cast
1195
+ end
1196
+
1197
+ def test_validates_acceptance_of_with_custom_error_using_quotes
1198
+ repair_validations(Developer) do
1199
+ Developer.validates_acceptance_of :salary, :message=> "This string contains 'single' and \"double\" quotes"
1200
+ d = Developer.new
1201
+ d.salary = "0"
1202
+ assert !d.valid?
1203
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
1204
+ end
1205
+ end
1206
+
1207
+ def test_validates_confirmation_of_with_custom_error_using_quotes
1208
+ repair_validations(Developer) do
1209
+ Developer.validates_confirmation_of :name, :message=> "confirm 'single' and \"double\" quotes"
1210
+ d = Developer.new
1211
+ d.name = "John"
1212
+ d.name_confirmation = "Johnny"
1213
+ assert !d.valid?
1214
+ assert_equal "confirm 'single' and \"double\" quotes", d.errors.on(:name)
1215
+ end
1216
+ end
1217
+
1218
+ def test_validates_format_of_with_custom_error_using_quotes
1219
+ repair_validations(Developer) do
1220
+ Developer.validates_format_of :name, :with => /^(A-Z*)$/, :message=> "format 'single' and \"double\" quotes"
1221
+ d = Developer.new
1222
+ d.name = d.name_confirmation = "John 32"
1223
+ assert !d.valid?
1224
+ assert_equal "format 'single' and \"double\" quotes", d.errors.on(:name)
1225
+ end
1226
+ end
1227
+
1228
+ def test_validates_inclusion_of_with_custom_error_using_quotes
1229
+ repair_validations(Developer) do
1230
+ Developer.validates_inclusion_of :salary, :in => 1000..80000, :message=> "This string contains 'single' and \"double\" quotes"
1231
+ d = Developer.new
1232
+ d.salary = "90,000"
1233
+ assert !d.valid?
1234
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
1235
+ end
1236
+ end
1237
+
1238
+ def test_validates_length_of_with_custom_too_long_using_quotes
1239
+ repair_validations(Developer) do
1240
+ Developer.validates_length_of :name, :maximum => 4, :too_long=> "This string contains 'single' and \"double\" quotes"
1241
+ d = Developer.new
1242
+ d.name = "Jeffrey"
1243
+ assert !d.valid?
1244
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name)
1245
+ end
1246
+ end
1247
+
1248
+ def test_validates_length_of_with_custom_too_short_using_quotes
1249
+ repair_validations(Developer) do
1250
+ Developer.validates_length_of :name, :minimum => 4, :too_short=> "This string contains 'single' and \"double\" quotes"
1251
+ d = Developer.new
1252
+ d.name = "Joe"
1253
+ assert !d.valid?
1254
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name)
1255
+ end
1256
+ end
1257
+
1258
+ def test_validates_length_of_with_custom_message_using_quotes
1259
+ repair_validations(Developer) do
1260
+ Developer.validates_length_of :name, :minimum => 4, :message=> "This string contains 'single' and \"double\" quotes"
1261
+ d = Developer.new
1262
+ d.name = "Joe"
1263
+ assert !d.valid?
1264
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name)
1265
+ end
1266
+ end
1267
+
1268
+ def test_validates_presence_of_with_custom_message_using_quotes
1269
+ repair_validations(Developer) do
1270
+ Developer.validates_presence_of :non_existent, :message=> "This string contains 'single' and \"double\" quotes"
1271
+ d = Developer.new
1272
+ d.name = "Joe"
1273
+ assert !d.valid?
1274
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:non_existent)
1275
+ end
1276
+ end
1277
+
1278
+ def test_validates_uniqueness_of_with_custom_message_using_quotes
1279
+ repair_validations(Developer) do
1280
+ Developer.validates_uniqueness_of :name, :message=> "This string contains 'single' and \"double\" quotes"
1281
+ d = Developer.new
1282
+ d.name = "David"
1283
+ assert !d.valid?
1284
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name)
1285
+ end
1286
+ end
1287
+
1288
+ def test_validates_associated_with_custom_message_using_quotes
1289
+ repair_validations(Reply) do
1290
+ Reply.validates_associated :topic, :message=> "This string contains 'single' and \"double\" quotes"
1291
+ Topic.validates_presence_of :content
1292
+ r = Reply.create("title" => "A reply", "content" => "with content!")
1293
+ r.topic = Topic.create("title" => "uhohuhoh")
1294
+ assert !r.valid?
1295
+ assert_equal "This string contains 'single' and \"double\" quotes", r.errors.on(:topic)
1296
+ end
1297
+ end
1298
+
1299
+ def test_if_validation_using_method_true
1300
+ # When the method returns true
1301
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => :condition_is_true )
1302
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1303
+ assert !t.valid?
1304
+ assert t.errors.on(:title)
1305
+ assert_equal "hoo 5", t.errors["title"]
1306
+ end
1307
+
1308
+ def test_unless_validation_using_method_true
1309
+ # When the method returns true
1310
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => :condition_is_true )
1311
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1312
+ assert t.valid?
1313
+ assert !t.errors.on(:title)
1314
+ end
1315
+
1316
+ def test_if_validation_using_method_false
1317
+ # When the method returns false
1318
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => :condition_is_true_but_its_not )
1319
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1320
+ assert t.valid?
1321
+ assert !t.errors.on(:title)
1322
+ end
1323
+
1324
+ def test_unless_validation_using_method_false
1325
+ # When the method returns false
1326
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => :condition_is_true_but_its_not )
1327
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1328
+ assert !t.valid?
1329
+ assert t.errors.on(:title)
1330
+ assert_equal "hoo 5", t.errors["title"]
1331
+ end
1332
+
1333
+ def test_if_validation_using_string_true
1334
+ # When the evaluated string returns true
1335
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => "a = 1; a == 1" )
1336
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1337
+ assert !t.valid?
1338
+ assert t.errors.on(:title)
1339
+ assert_equal "hoo 5", t.errors["title"]
1340
+ end
1341
+
1342
+ def test_unless_validation_using_string_true
1343
+ # When the evaluated string returns true
1344
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => "a = 1; a == 1" )
1345
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1346
+ assert t.valid?
1347
+ assert !t.errors.on(:title)
1348
+ end
1349
+
1350
+ def test_if_validation_using_string_false
1351
+ # When the evaluated string returns false
1352
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => "false")
1353
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1354
+ assert t.valid?
1355
+ assert !t.errors.on(:title)
1356
+ end
1357
+
1358
+ def test_unless_validation_using_string_false
1359
+ # When the evaluated string returns false
1360
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => "false")
1361
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1362
+ assert !t.valid?
1363
+ assert t.errors.on(:title)
1364
+ assert_equal "hoo 5", t.errors["title"]
1365
+ end
1366
+
1367
+ def test_if_validation_using_block_true
1368
+ # When the block returns true
1369
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
1370
+ :if => Proc.new { |r| r.content.size > 4 } )
1371
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1372
+ assert !t.valid?
1373
+ assert t.errors.on(:title)
1374
+ assert_equal "hoo 5", t.errors["title"]
1375
+ end
1376
+
1377
+ def test_unless_validation_using_block_true
1378
+ # When the block returns true
1379
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
1380
+ :unless => Proc.new { |r| r.content.size > 4 } )
1381
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1382
+ assert t.valid?
1383
+ assert !t.errors.on(:title)
1384
+ end
1385
+
1386
+ def test_if_validation_using_block_false
1387
+ # When the block returns false
1388
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
1389
+ :if => Proc.new { |r| r.title != "uhohuhoh"} )
1390
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1391
+ assert t.valid?
1392
+ assert !t.errors.on(:title)
1393
+ end
1394
+
1395
+ def test_unless_validation_using_block_false
1396
+ # When the block returns false
1397
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
1398
+ :unless => Proc.new { |r| r.title != "uhohuhoh"} )
1399
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1400
+ assert !t.valid?
1401
+ assert t.errors.on(:title)
1402
+ assert_equal "hoo 5", t.errors["title"]
1403
+ end
1404
+
1405
+ def test_validates_associated_missing
1406
+ repair_validations(Reply) do
1407
+ Reply.validates_presence_of(:topic)
1408
+ r = Reply.create("title" => "A reply", "content" => "with content!")
1409
+ assert !r.valid?
1410
+ assert r.errors.on(:topic)
1411
+
1412
+ r.topic = Topic.find :first
1413
+ assert r.valid?
1414
+ end
1415
+ end
1416
+
1417
+ def test_errors_to_xml
1418
+ r = Reply.new :title => "Wrong Create"
1419
+ assert !r.valid?
1420
+ xml = r.errors.to_xml(:skip_instruct => true)
1421
+ assert_equal "<errors>", xml.first(8)
1422
+ assert xml.include?("<error>Title is Wrong Create</error>")
1423
+ assert xml.include?("<error>Content Empty</error>")
1424
+ end
1425
+
1426
+ def test_validation_order
1427
+ Topic.validates_presence_of :title
1428
+ Topic.validates_length_of :title, :minimum => 2
1429
+
1430
+ t = Topic.new("title" => "")
1431
+ assert !t.valid?
1432
+ assert_equal "can't be blank", t.errors.on("title").first
1433
+ end
1434
+
1435
+ def test_invalid_should_be_the_opposite_of_valid
1436
+ Topic.validates_presence_of :title
1437
+
1438
+ t = Topic.new
1439
+ assert t.invalid?
1440
+ assert t.errors.invalid?(:title)
1441
+
1442
+ t.title = 'Things are going to change'
1443
+ assert !t.invalid?
1444
+ end
1445
+
1446
+ # previous implementation of validates_presence_of eval'd the
1447
+ # string with the wrong binding, this regression test is to
1448
+ # ensure that it works correctly
1449
+ def test_validation_with_if_as_string
1450
+ Topic.validates_presence_of(:title)
1451
+ Topic.validates_presence_of(:author_name, :if => "title.to_s.match('important')")
1452
+
1453
+ t = Topic.new
1454
+ assert !t.valid?, "A topic without a title should not be valid"
1455
+ assert !t.errors.invalid?("author_name"), "A topic without an 'important' title should not require an author"
1456
+
1457
+ t.title = "Just a title"
1458
+ assert t.valid?, "A topic with a basic title should be valid"
1459
+
1460
+ t.title = "A very important title"
1461
+ assert !t.valid?, "A topic with an important title, but without an author, should not be valid"
1462
+ assert t.errors.invalid?("author_name"), "A topic with an 'important' title should require an author"
1463
+
1464
+ t.author_name = "Hubert J. Farnsworth"
1465
+ assert t.valid?, "A topic with an important title and author should be valid"
1466
+ end
1467
+ end
1468
+
1469
+
1470
+ class ValidatesNumericalityTest < ActiveRecord::TestCase
1471
+ NIL = [nil]
1472
+ BLANK = ["", " ", " \t \r \n"]
1473
+ BIGDECIMAL_STRINGS = %w(12345678901234567890.1234567890) # 30 significent digits
1474
+ FLOAT_STRINGS = %w(0.0 +0.0 -0.0 10.0 10.5 -10.5 -0.0001 -090.1 90.1e1 -90.1e5 -90.1e-5 90e-5)
1475
+ INTEGER_STRINGS = %w(0 +0 -0 10 +10 -10 0090 -090)
1476
+ FLOATS = [0.0, 10.0, 10.5, -10.5, -0.0001] + FLOAT_STRINGS
1477
+ INTEGERS = [0, 10, -10] + INTEGER_STRINGS
1478
+ BIGDECIMAL = BIGDECIMAL_STRINGS.collect! { |bd| BigDecimal.new(bd) }
1479
+ JUNK = ["not a number", "42 not a number", "0xdeadbeef", "00-1", "--3", "+-3", "+3-1", "-+019.0", "12.12.13.12", "123\nnot a number"]
1480
+ INFINITY = [1.0/0.0]
1481
+
1482
+ repair_validations(Topic)
1483
+
1484
+ def test_default_validates_numericality_of
1485
+ Topic.validates_numericality_of :approved
1486
+
1487
+ invalid!(NIL + BLANK + JUNK)
1488
+ valid!(FLOATS + INTEGERS + BIGDECIMAL + INFINITY)
1489
+ end
1490
+
1491
+ def test_validates_numericality_of_with_nil_allowed
1492
+ Topic.validates_numericality_of :approved, :allow_nil => true
1493
+
1494
+ invalid!(JUNK)
1495
+ valid!(NIL + BLANK + FLOATS + INTEGERS + BIGDECIMAL + INFINITY)
1496
+ end
1497
+
1498
+ def test_validates_numericality_of_with_integer_only
1499
+ Topic.validates_numericality_of :approved, :only_integer => true
1500
+
1501
+ invalid!(NIL + BLANK + JUNK + FLOATS + BIGDECIMAL + INFINITY)
1502
+ valid!(INTEGERS)
1503
+ end
1504
+
1505
+ def test_validates_numericality_of_with_integer_only_and_nil_allowed
1506
+ Topic.validates_numericality_of :approved, :only_integer => true, :allow_nil => true
1507
+
1508
+ invalid!(JUNK + FLOATS + BIGDECIMAL + INFINITY)
1509
+ valid!(NIL + BLANK + INTEGERS)
1510
+ end
1511
+
1512
+ def test_validates_numericality_with_greater_than
1513
+ Topic.validates_numericality_of :approved, :greater_than => 10
1514
+
1515
+ invalid!([-10, 10], 'must be greater than 10')
1516
+ valid!([11])
1517
+ end
1518
+
1519
+ def test_validates_numericality_with_greater_than_or_equal
1520
+ Topic.validates_numericality_of :approved, :greater_than_or_equal_to => 10
1521
+
1522
+ invalid!([-9, 9], 'must be greater than or equal to 10')
1523
+ valid!([10])
1524
+ end
1525
+
1526
+ def test_validates_numericality_with_equal_to
1527
+ Topic.validates_numericality_of :approved, :equal_to => 10
1528
+
1529
+ invalid!([-10, 11] + INFINITY, 'must be equal to 10')
1530
+ valid!([10])
1531
+ end
1532
+
1533
+ def test_validates_numericality_with_less_than
1534
+ Topic.validates_numericality_of :approved, :less_than => 10
1535
+
1536
+ invalid!([10], 'must be less than 10')
1537
+ valid!([-9, 9])
1538
+ end
1539
+
1540
+ def test_validates_numericality_with_less_than_or_equal_to
1541
+ Topic.validates_numericality_of :approved, :less_than_or_equal_to => 10
1542
+
1543
+ invalid!([11], 'must be less than or equal to 10')
1544
+ valid!([-10, 10])
1545
+ end
1546
+
1547
+ def test_validates_numericality_with_odd
1548
+ Topic.validates_numericality_of :approved, :odd => true
1549
+
1550
+ invalid!([-2, 2], 'must be odd')
1551
+ valid!([-1, 1])
1552
+ end
1553
+
1554
+ def test_validates_numericality_with_even
1555
+ Topic.validates_numericality_of :approved, :even => true
1556
+
1557
+ invalid!([-1, 1], 'must be even')
1558
+ valid!([-2, 2])
1559
+ end
1560
+
1561
+ def test_validates_numericality_with_greater_than_less_than_and_even
1562
+ Topic.validates_numericality_of :approved, :greater_than => 1, :less_than => 4, :even => true
1563
+
1564
+ invalid!([1, 3, 4])
1565
+ valid!([2])
1566
+ end
1567
+
1568
+ def test_validates_numericality_with_numeric_message
1569
+ Topic.validates_numericality_of :approved, :less_than => 4, :message => "smaller than {{count}}"
1570
+ topic = Topic.new("title" => "numeric test", "approved" => 10)
1571
+
1572
+ assert !topic.valid?
1573
+ assert_equal "smaller than 4", topic.errors.on(:approved)
1574
+
1575
+ Topic.validates_numericality_of :approved, :greater_than => 4, :message => "greater than {{count}}"
1576
+ topic = Topic.new("title" => "numeric test", "approved" => 1)
1577
+
1578
+ assert !topic.valid?
1579
+ assert_equal "greater than 4", topic.errors.on(:approved)
1580
+ end
1581
+
1582
+ private
1583
+ def invalid!(values, error=nil)
1584
+ with_each_topic_approved_value(values) do |topic, value|
1585
+ assert !topic.valid?, "#{value.inspect} not rejected as a number"
1586
+ assert topic.errors.on(:approved)
1587
+ assert_equal error, topic.errors.on(:approved) if error
1588
+ end
1589
+ end
1590
+
1591
+ def valid!(values)
1592
+ with_each_topic_approved_value(values) do |topic, value|
1593
+ assert topic.valid?, "#{value.inspect} not accepted as a number"
1594
+ end
1595
+ end
1596
+
1597
+ def with_each_topic_approved_value(values)
1598
+ topic = Topic.new("title" => "numeric test", "content" => "whatever")
1599
+ values.each do |value|
1600
+ topic.approved = value
1601
+ yield topic, value
1602
+ end
1603
+ end
1604
+ end