amount_field_rails3 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ class TestProduct < ActiveRecord::Base
2
+
3
+ validates_amount_format_of :price
4
+ validates_numericality_of :price, :message => "not numericality"
5
+ validates_presence_of :price
6
+
7
+ end
@@ -0,0 +1,63 @@
1
+ ENV["RAILS_ENV"] = "test"
2
+ require File.expand_path(File.dirname(__FILE__) + "/../../../../config/environment")
3
+ require 'rails/test_help'
4
+
5
+ require File.join(File.dirname(__FILE__), '../init')
6
+ Dir[File.join(File.dirname(__FILE__), 'models/*.rb')].each { |f| require f }
7
+
8
+ ActiveRecord::Base.establish_connection({
9
+ :adapter => 'mysql2',
10
+ :database => 'gem_amount_field_test',
11
+ :host => 'localhost',
12
+ :username => 'root',
13
+ :password => ''
14
+ })
15
+
16
+ ActiveRecord::Migration.verbose = false
17
+ ActiveRecord::Schema.define(:version => 1) do
18
+ create_table "test_products", :force => true do |t|
19
+ t.column "name", :string
20
+ t.column "price", :decimal, :precision => 12, :scale => 2
21
+ t.column "stock_price", :decimal, :precision => 12, :scale => 2
22
+ t.column "float_price", :float
23
+ end
24
+ end
25
+
26
+ class ActiveSupport::TestCase
27
+
28
+ def with_configuration(config)
29
+ begin
30
+ orig_config = AmountField::ActiveRecord::Validations.configuration
31
+ AmountField::ActiveRecord::Validations.configuration = config
32
+ yield
33
+ ensure
34
+ AmountField::ActiveRecord::Validations.configuration = orig_config
35
+ end
36
+ end
37
+
38
+ def with_locale(locale)
39
+ begin
40
+ orig_locale = I18n.locale
41
+ I18n.locale = locale
42
+ yield
43
+ ensure
44
+ I18n.locale = orig_locale
45
+ end
46
+ end
47
+
48
+ def assert_valid_formats(formats, test_clazz)
49
+ formats.each do |format, expected_value|
50
+ product = test_clazz.new(:amount_field_price => format)
51
+ assert product.valid?, "expected '#{format}' to be valid (#{product.errors.full_messages.inspect})"
52
+ assert_in_delta expected_value, product.price.to_f, 0.001
53
+ end
54
+ end
55
+
56
+ def assert_invalid_formats(formats, test_clazz)
57
+ formats.each do |format|
58
+ product = test_clazz.new(:amount_field_price => format)
59
+ assert !product.valid?, "expected '#{format}' to be invalid (#{product.errors.full_messages.inspect})"
60
+ end
61
+ end
62
+
63
+ end
@@ -0,0 +1,389 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+
5
+ class ValidationsTest < ActiveSupport::TestCase
6
+
7
+ # allows stuff like de_test_values.with_minus.to_us
8
+ class ::Hash
9
+ def with_plus
10
+ self.inject({}) { |h, (k, v)| h["+#{k}"] = v; h }
11
+ end
12
+
13
+ def with_minus
14
+ self.inject({}) { |h, (k, v)| h["-#{k}"] = (v*-1.0); h }
15
+ end
16
+
17
+ def to_us
18
+ self.inject({}) { |h, (k, v)| h[k.sub(',', ';').gsub('.', ',').sub(';', '.')] = v; h }
19
+ end
20
+ end
21
+
22
+ def de_test_values
23
+ {
24
+ '0' => 0.0,
25
+ ',0' => 0.0,
26
+ ',00' => 0.0,
27
+ '1' => 1.0,
28
+ '1,' => 1.0,
29
+ '1,0' => 1.0,
30
+ '1,00' => 1.0,
31
+ '12' => 12.0,
32
+ '12,' => 12.0,
33
+ '12,0' => 12.0,
34
+ '12,00' => 12.0,
35
+ '123' => 123.0,
36
+ '123,' => 123.0,
37
+ '123,0' => 123.0,
38
+ '123,00' => 123.0,
39
+ '1234' => 1234.0,
40
+ '1234,' => 1234.0,
41
+ '1234,0' => 1234.0,
42
+ '1234,00' => 1234.0,
43
+ '1234,56' => 1234.56,
44
+ '1.234,56' => 1234.56,
45
+ '12345,67' => 12345.67,
46
+ '12.345,67' => 12345.67,
47
+ '123456,78' => 123456.78,
48
+ '123.456,78' => 123456.78,
49
+ '1234567,89' => 1234567.89,
50
+ '1.234.567,89' => 1234567.89
51
+ }
52
+ end
53
+
54
+
55
+ test "orignal setter accept ruby value if set via new" do
56
+ product = TestProduct.new(:price => 12.34)
57
+ assert_in_delta 12.34, product.price, 0.001
58
+ end
59
+
60
+ test "orignal setter accept ruby value" do
61
+ product = TestProduct.new
62
+ product.price = 12.34
63
+ assert_in_delta 12.34, product.price, 0.001
64
+ end
65
+
66
+ test "price is valid for loaded record" do
67
+ product = TestProduct.create!(:price => 12.34)
68
+ assert_in_delta 12.34, product.price, 0.001
69
+ end
70
+
71
+ test "special setter does not accept a decimal value" do
72
+ # Special case: The value is assign via the special setter as a decimal not as a string.
73
+ # The validation fails because the value is not of the german format '1234,56' and therefore
74
+ # the price should be 0.0 (not assiged) but is 1234.56
75
+ product = TestProduct.new
76
+ product.amount_field_price = 1234.56
77
+ assert !product.valid?, "expect 1234.56 to be invalid (#{product.errors.full_messages.inspect})"
78
+ assert_in_delta 1234.56, product.price.to_f, 0.001
79
+ end
80
+
81
+ test "special setter accept german format value" do
82
+ with_locale('de') do
83
+ product = TestProduct.new
84
+ product.amount_field_price = '1.234,56'
85
+ assert product.valid?, "expect '1.234,56' to be valid (#{product.errors.full_messages.inspect})"
86
+ assert_in_delta 1234.56, product.price, 0.001
87
+ end
88
+ end
89
+
90
+ test "accept valid german formats" do
91
+ class TestProductValidGermanFormat < ActiveRecord::Base
92
+ set_table_name 'test_products'
93
+ validates_amount_format_of :price
94
+ end
95
+
96
+ with_locale('de') do
97
+ assert_valid_formats(de_test_values, TestProductValidGermanFormat)
98
+ assert_valid_formats(de_test_values.with_minus, TestProductValidGermanFormat) # -value
99
+ assert_valid_formats(de_test_values.with_plus, TestProductValidGermanFormat) # +value
100
+ end
101
+ end
102
+
103
+ test "accept valid us formats" do
104
+ with_configuration({ :separator => '.', :delimiter => ',', :precision => 2 }) do
105
+ class TestProductValidUsFormat < ActiveRecord::Base
106
+ set_table_name 'test_products'
107
+ validates_amount_format_of :price
108
+ end
109
+
110
+ with_locale('de') do
111
+ assert_valid_formats(de_test_values.to_us, TestProductValidUsFormat)
112
+ assert_valid_formats(de_test_values.to_us.with_minus, TestProductValidUsFormat) # -value
113
+ assert_valid_formats(de_test_values.to_us.with_plus, TestProductValidUsFormat) # +value
114
+ end
115
+ end
116
+ end
117
+
118
+ test "dont accept invalid formats" do
119
+ class TestProductInValidGermanFormat < ActiveRecord::Base
120
+ set_table_name 'test_products'
121
+ validates_amount_format_of :price
122
+ end
123
+
124
+ with_locale('de') do
125
+ assert_invalid_formats(['1234.567.890,12', '123.4567.890,12', '1,2.34', '1,2,3', '1.23,45',
126
+ '1.23.45,6', '2,1x', '2,x', 'x2', '++1,23', '+-1,23', '--1,23', '-+1,23', nil],
127
+ TestProductInValidGermanFormat)
128
+ end
129
+ end
130
+
131
+ test "accept only values of format with the defined configuration" do
132
+ class TestProductConfiguration < ActiveRecord::Base
133
+ set_table_name 'test_products'
134
+ validates_amount_format_of :price, :separator => '.', :delimiter => ','
135
+ validates_amount_format_of :stock_price, :separator => ',', :delimiter => '.'
136
+ end
137
+
138
+ product = TestProductConfiguration.new(:amount_field_price => '1,234,567.89',
139
+ :amount_field_stock_price => '1.234.567,89')
140
+ assert product.valid?
141
+ assert_in_delta 1234567.89, product.price, 0.001
142
+ assert_in_delta 1234567.89, product.stock_price, 0.001
143
+ end
144
+
145
+ test "accept only values of format with the defined precision" do
146
+ class TestProductPrecision < ActiveRecord::Base
147
+ set_table_name 'test_products'
148
+ validates_amount_format_of :price # default 2
149
+ validates_amount_format_of :stock_price, :precision => 1
150
+ end
151
+
152
+ product = TestProductPrecision.new(:amount_field_price => '1,234.56',
153
+ :amount_field_stock_price => '1,234.5')
154
+ assert product.valid?
155
+ assert_in_delta 1234.56, product.price, 0.001
156
+ assert_in_delta 1234.50, product.stock_price, 0.001
157
+ end
158
+
159
+ test "accept only integer if precision is 0 or nil" do
160
+ class TestProductInteger < ActiveRecord::Base
161
+ set_table_name 'test_products'
162
+ validates_amount_format_of :price, :precision => 0, :separator => nil
163
+ end
164
+
165
+ with_locale('de') do
166
+ assert_valid_formats({'0' => 0.0, '1' => 1.0, '12' => 12.0, '123' => 123.0, '1.234' => 1234.0}, TestProductInteger)
167
+ assert_invalid_formats(['1,', '1,0', '1,2', '1,23'], TestProductInteger)
168
+ end
169
+ end
170
+
171
+ test "accept only values with no delimiter" do
172
+ class TestProductNoDelimiter < ActiveRecord::Base
173
+ set_table_name 'test_products'
174
+ validates_amount_format_of :price, :separator => '.', :delimiter => nil
175
+ end
176
+
177
+ with_locale('de') do
178
+ assert_valid_formats({'.00' => 0.0, '1.00' => 1.0, '12.00' => 12.0, '123.00' => 123.0, '1234.00' => 1234.0, '12345.00' => 12345.0, '123456.00' => 123456.0}, TestProductNoDelimiter)
179
+ assert_invalid_formats(['1,234.56', '123,456.00'], TestProductNoDelimiter)
180
+ end
181
+ end
182
+
183
+ test "use english default message" do
184
+ class TestProductEnglishMessage < ActiveRecord::Base
185
+ set_table_name 'test_products'
186
+ validates_amount_format_of :price
187
+ end
188
+
189
+ with_locale('en') do
190
+ product = TestProductEnglishMessage.new(:amount_field_price => 'x')
191
+ assert !product.valid?
192
+ assert_equal "'x' is not a valid amount format (d,ddd.dd)", product.errors[:price].first
193
+ end
194
+ end
195
+
196
+ test "use german default message" do
197
+ class TestProductGermanMessage < ActiveRecord::Base
198
+ set_table_name 'test_products'
199
+ validates_amount_format_of :price
200
+ end
201
+
202
+ with_locale('de') do
203
+ product = TestProductGermanMessage.new(:amount_field_price => 'x')
204
+ assert !product.valid?
205
+ assert_equal "'x' ist ein ungültiges Format (d.ddd,dd)", product.errors[:price].first
206
+ end
207
+ end
208
+
209
+ test "validates_amount_format_of use given message" do
210
+ class TestProductGivenMessage < ActiveRecord::Base
211
+ set_table_name 'test_products'
212
+ validates_amount_format_of :price, :message => "special message %{value}"
213
+ end
214
+
215
+ product = TestProductGivenMessage.new(:amount_field_price => 'x')
216
+ assert !product.valid?
217
+ assert_equal "special message x", product.errors[:price].first
218
+ end
219
+
220
+ test "matches if precision is right" do
221
+ class TestProductValidPrecision < ActiveRecord::Base
222
+ set_table_name 'test_products'
223
+ validates_amount_format_of :price, :precision => 1
224
+ end
225
+
226
+ with_locale('de') do
227
+ [ ',0', '1,0', '1,0'].each do |value|
228
+ assert TestProductValidPrecision.new(:amount_field_price => value).valid?, "expected '#{value}' to be valid"
229
+ end
230
+ end
231
+ end
232
+
233
+ test "explicit definition overwrites default configuration" do
234
+ with_configuration(default = { :separator => '_', :delimiter => ';', :precision => 2 }) do
235
+ class TestProductOverwriteConfiguration < ActiveRecord::Base
236
+ set_table_name 'test_products'
237
+ validates_amount_format_of :price, :separator => '/', :delimiter => '@'
238
+ validates_amount_format_of :stock_price
239
+ end
240
+
241
+ [ '/00', '1/00', '12/00', '123/00', '1234/00', '1@234/00', '12@345/00', '123@456/00', '1@234@567/00'
242
+ ].each do |param|
243
+ param2 = param.gsub('/', '_').gsub('@', ';')
244
+ product = TestProductOverwriteConfiguration.new(:amount_field_price => param, :amount_field_stock_price => param2)
245
+ assert product.valid?, product.errors.full_messages.inspect
246
+ assert_in_delta Float(param.gsub('@', '').sub('/', '.')), product.price, 0.001
247
+ end
248
+ end
249
+ end
250
+
251
+ test "nil or blank value is not valid by default" do
252
+ class TestProductNilOrBlankNotValidByDefault < ActiveRecord::Base
253
+ set_table_name 'test_products'
254
+ validates_amount_format_of :price
255
+ validates_amount_format_of :stock_price
256
+ end
257
+
258
+ product = TestProductNilOrBlankNotValidByDefault.new(:amount_field_price => nil, :amount_field_stock_price => "")
259
+ assert !product.valid?
260
+ assert_match /format/, product.errors[:price].first
261
+ assert_match /format/, product.errors[:stock_price].first
262
+ end
263
+
264
+ test "nil or blank value is valid if option :allow_nil or :allow_blank is set to true" do
265
+ class TestProductNilOrBlankValidIfAllowed < ActiveRecord::Base
266
+ set_table_name 'test_products'
267
+ validates_amount_format_of :price, :allow_nil => true
268
+ validates_amount_format_of :stock_price, :allow_blank => true
269
+ end
270
+
271
+ product = TestProductNilOrBlankValidIfAllowed.new(:amount_field_price => nil, :amount_field_stock_price => "")
272
+ assert product.valid?, product.errors.full_messages.inspect
273
+ assert_nil product.price
274
+ end
275
+
276
+ test "valid format example returns string depending on given configuration" do
277
+ assert_equal "d.ddd,dd", TestProduct.send(:valid_format_example, { :precision => 2, :delimiter => '.', :separator => ',' })
278
+ assert_equal "d,ddd.dd", TestProduct.send(:valid_format_example, { :precision => 2, :delimiter => ',', :separator => '.' })
279
+ assert_equal "dddd,dd", TestProduct.send(:valid_format_example, { :precision => 2, :delimiter => nil, :separator => ',' })
280
+ assert_equal "dddd,d", TestProduct.send(:valid_format_example, { :precision => 1, :delimiter => nil, :separator => ',' })
281
+ assert_equal "dddd", TestProduct.send(:valid_format_example, { :precision => nil, :delimiter => nil, :separator => nil })
282
+ assert_equal "d.ddd", TestProduct.send(:valid_format_example, { :precision => nil, :delimiter => '.', :separator => nil })
283
+ end
284
+
285
+ test "default prefix is use for special setter name" do
286
+ class TestProductDefaultPrefix < ActiveRecord::Base
287
+ set_table_name 'test_products'
288
+ validates_amount_format_of :price
289
+ end
290
+
291
+ assert TestProductDefaultPrefix.new.respond_to?("#{AmountField::Configuration.prefix}_price=")
292
+ end
293
+
294
+ test "configured prefix is use for special setter name" do
295
+ AmountField::Configuration.prefix = 'my_prefix'
296
+ class TestProductConfigPrefix < ActiveRecord::Base
297
+ set_table_name 'test_products'
298
+ validates_amount_format_of :price
299
+ end
300
+
301
+ assert TestProductConfigPrefix.new.respond_to?('my_prefix_price=')
302
+ AmountField::Configuration.prefix = 'amount_field'
303
+ end
304
+
305
+ test "works with float attribute" do
306
+ class TestProductFloatAttribute < ActiveRecord::Base
307
+ set_table_name 'test_products'
308
+ validates_amount_format_of :float_price
309
+ end
310
+ with_locale('de') do
311
+ assert TestProductFloatAttribute.new(:amount_field_float_price => '1.234,00').valid?
312
+ assert TestProductFloatAttribute.new(:amount_field_float_price => '1234').valid?
313
+ end
314
+ end
315
+
316
+ test "allow separator and delimiter to be optional for german format" do
317
+ class TestProductOptionalSeparatorAndDelimiterForGerman < ActiveRecord::Base
318
+ set_table_name 'test_products'
319
+ validates_amount_format_of :float_price
320
+ end
321
+ with_locale('de') do
322
+ ['1.234,00', '1234,00', '1.234', '1234'].each do |v|
323
+ p = TestProductOptionalSeparatorAndDelimiterForGerman.new(:amount_field_float_price => v)
324
+ assert p.valid?, p.errors.full_messages.inspect
325
+ end
326
+ end
327
+ end
328
+
329
+ test "allow separator and delimiter to be optional for us format" do
330
+ class TestProductOptionalSeparatorAndDelimiterForUs < ActiveRecord::Base
331
+ set_table_name 'test_products'
332
+ validates_amount_format_of :float_price
333
+ end
334
+ with_locale('en') do
335
+ ['1,234.00', '1234.00', '1,234', '1234'].each do |v|
336
+ p = TestProductOptionalSeparatorAndDelimiterForUs.new(:amount_field_float_price => v)
337
+ assert p.valid?, p.errors.full_messages.inspect
338
+ end
339
+ end
340
+ end
341
+
342
+ test "an invalid value is still invalid after multiple validation" do
343
+ class TestProductValidValid < ActiveRecord::Base
344
+ set_table_name 'test_products'
345
+ validates_amount_format_of :price
346
+ end
347
+ p = TestProductValidValid.new(:amount_field_price => "x")
348
+ assert !p.valid?
349
+ assert !p.valid?
350
+ end
351
+
352
+ test "a value is not invalid after multiple validation" do
353
+ with_locale('de') do
354
+ class TestProductValidValid < ActiveRecord::Base
355
+ set_table_name 'test_products'
356
+ validates_amount_format_of :price
357
+ end
358
+ p = TestProductValidValid.new(:amount_field_price => "1.234,56")
359
+ assert p.valid?
360
+ assert p.valid?
361
+ end
362
+ end
363
+
364
+ test "calling create with an invalid value does not save the model" do
365
+ class TestProductViaCreate< ActiveRecord::Base
366
+ set_table_name 'test_products'
367
+ validates_amount_format_of :price
368
+ end
369
+ assert_no_difference 'TestProduct.count' do
370
+ p = TestProductViaCreate.create(:amount_field_price => "x")
371
+ assert p.new_record?
372
+ assert !p.valid?
373
+ end
374
+ end
375
+
376
+ test "another validation macro validates the converted value" do
377
+ class TestProductWithOtherValidationMacros < ActiveRecord::Base
378
+ set_table_name 'test_products'
379
+ validates_amount_format_of :price
380
+ validates_numericality_of :price
381
+ end
382
+ p = TestProductWithOtherValidationMacros.new(:amount_field_price => "x")
383
+ assert !p.valid?
384
+ assert p.errors.full_messages.include?("Price 'x' is not a valid amount format (d,ddd.dd)")
385
+ assert p.errors.full_messages.include?("Price is not a number")
386
+ end
387
+
388
+ end
389
+