input_sanitizer 0.1.9 → 0.4.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 (48) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/gempush.yml +28 -0
  3. data/.gitignore +2 -1
  4. data/.travis.yml +4 -8
  5. data/CHANGELOG +96 -0
  6. data/LICENSE +201 -22
  7. data/README.md +22 -3
  8. data/input_sanitizer.gemspec +10 -4
  9. data/lib/input_sanitizer.rb +5 -2
  10. data/lib/input_sanitizer/errors.rb +142 -0
  11. data/lib/input_sanitizer/extended_converters.rb +5 -52
  12. data/lib/input_sanitizer/extended_converters/comma_joined_integers_converter.rb +15 -0
  13. data/lib/input_sanitizer/extended_converters/comma_joined_strings_converter.rb +15 -0
  14. data/lib/input_sanitizer/extended_converters/positive_integer_converter.rb +12 -0
  15. data/lib/input_sanitizer/extended_converters/specific_values_converter.rb +19 -0
  16. data/lib/input_sanitizer/restricted_hash.rb +49 -8
  17. data/lib/input_sanitizer/v1.rb +22 -0
  18. data/lib/input_sanitizer/v1/clean_field.rb +38 -0
  19. data/lib/input_sanitizer/{default_converters.rb → v1/default_converters.rb} +30 -13
  20. data/lib/input_sanitizer/v1/sanitizer.rb +166 -0
  21. data/lib/input_sanitizer/v2.rb +13 -0
  22. data/lib/input_sanitizer/v2/clean_field.rb +36 -0
  23. data/lib/input_sanitizer/v2/clean_payload_collection_field.rb +41 -0
  24. data/lib/input_sanitizer/v2/clean_query_collection_field.rb +40 -0
  25. data/lib/input_sanitizer/v2/error_collection.rb +49 -0
  26. data/lib/input_sanitizer/v2/nested_sanitizer_factory.rb +19 -0
  27. data/lib/input_sanitizer/v2/payload_sanitizer.rb +130 -0
  28. data/lib/input_sanitizer/v2/payload_transform.rb +42 -0
  29. data/lib/input_sanitizer/v2/query_sanitizer.rb +33 -0
  30. data/lib/input_sanitizer/v2/types.rb +213 -0
  31. data/lib/input_sanitizer/version.rb +1 -1
  32. data/spec/extended_converters/comma_joined_integers_converter_spec.rb +18 -0
  33. data/spec/extended_converters/comma_joined_strings_converter_spec.rb +18 -0
  34. data/spec/extended_converters/positive_integer_converter_spec.rb +18 -0
  35. data/spec/extended_converters/specific_values_converter_spec.rb +27 -0
  36. data/spec/restricted_hash_spec.rb +37 -7
  37. data/spec/sanitizer_spec.rb +129 -26
  38. data/spec/spec_helper.rb +17 -2
  39. data/spec/v1/default_converters_spec.rb +141 -0
  40. data/spec/v2/converters_spec.rb +174 -0
  41. data/spec/v2/payload_sanitizer_spec.rb +460 -0
  42. data/spec/v2/payload_transform_spec.rb +98 -0
  43. data/spec/v2/query_sanitizer_spec.rb +300 -0
  44. data/v2.md +52 -0
  45. metadata +105 -40
  46. data/lib/input_sanitizer/sanitizer.rb +0 -152
  47. data/spec/default_converters_spec.rb +0 -101
  48. data/spec/extended_converters_spec.rb +0 -62
@@ -1,7 +1,22 @@
1
1
  require 'bundler'
2
2
  Bundler.setup(:test)
3
3
 
4
- require 'simplecov'
5
- SimpleCov.start unless ENV['CI'] == 'true'
4
+ skip_coverage = ENV['CI']
5
+
6
+ unless skip_coverage
7
+ require 'simplecov'
8
+ SimpleCov.start
9
+ end
10
+
11
+ RSpec.configure do |config|
12
+ config.expect_with :rspec do |c|
13
+ c.syntax = :should
14
+ end
15
+
16
+ config.mock_with :rspec do |c|
17
+ c.syntax = :should
18
+ end
19
+ end
6
20
 
7
21
  require 'input_sanitizer'
22
+ require 'pry'
@@ -0,0 +1,141 @@
1
+ require 'spec_helper'
2
+
3
+ describe InputSanitizer::V1::IntegerConverter do
4
+ let(:converter) { InputSanitizer::V1::IntegerConverter.new }
5
+
6
+ it "casts string to integer" do
7
+ converter.call("42").should == 42
8
+ end
9
+
10
+ it "casts integer to integer" do
11
+ converter.call(42).should == 42
12
+ end
13
+
14
+ it "raises error if cannot cast" do
15
+ lambda { converter.call("f") }.should raise_error(InputSanitizer::ConversionError)
16
+ end
17
+ end
18
+
19
+ describe InputSanitizer::V1::DateConverter do
20
+ let(:converter) { InputSanitizer::V1::DateConverter.new }
21
+
22
+ it "casts dates in iso format" do
23
+ converter.call("2012-05-15").should == Date.new(2012, 5, 15)
24
+ end
25
+
26
+ it "raises error if cannot cast" do
27
+ lambda { converter.call("2012-02-30") }.should raise_error(InputSanitizer::ConversionError)
28
+ end
29
+ end
30
+
31
+ describe InputSanitizer::V1::BooleanConverter do
32
+ let(:converter) { InputSanitizer::V1::BooleanConverter.new }
33
+
34
+ it "casts 'true' to true" do
35
+ converter.call('true').should eq(true)
36
+ end
37
+
38
+ it "casts true to true" do
39
+ converter.call(true).should eq(true)
40
+ end
41
+
42
+ it "casts '1' to true" do
43
+ converter.call('1').should eq(true)
44
+ end
45
+
46
+ it "casts 'yes' to true" do
47
+ converter.call('yes').should eq(true)
48
+ end
49
+
50
+ it "casts 'false' to false" do
51
+ converter.call('false').should eq(false)
52
+ end
53
+
54
+ it "casts false to false" do
55
+ converter.call(false).should eq(false)
56
+ end
57
+
58
+ it "casts '0' to false" do
59
+ converter.call('0').should eq(false)
60
+ end
61
+
62
+ it "casts 'no' to false" do
63
+ converter.call('no').should eq(false)
64
+ end
65
+
66
+ it "raises error if cannot cast" do
67
+ lambda { converter.call("notboolean") }.should raise_error(InputSanitizer::ConversionError)
68
+ end
69
+ end
70
+
71
+ describe InputSanitizer::V1::TimeConverter do
72
+ let(:converter) { InputSanitizer::V1::TimeConverter.new }
73
+
74
+ it "raises if timezone part given" do
75
+ lambda { converter.call("2012-05-15 13:42:54 +01:00") }.should raise_error(InputSanitizer::ConversionError)
76
+ end
77
+
78
+ it "casts date time in iso format" do
79
+ t = Time.utc(2012, 5, 15, 13, 42, 54)
80
+ converter.call("2012-05-15 13:42:54").should == t
81
+ converter.call("2012-05-15T13:42:54").should == t
82
+ converter.call("20120515134254").should == t
83
+ end
84
+
85
+ it "works with miliseconds" do
86
+ t = Time.utc(2012, 5, 15, 13, 42, 54)
87
+ converter.call("2012-05-15 13:42:54.000").should == t
88
+ converter.call("2012-05-15T13:42:54.000").should == t
89
+ converter.call("20120515134254000").should == t
90
+ end
91
+
92
+ it "works with Z at the end" do
93
+ t = Time.utc(2012, 5, 15, 13, 42, 54)
94
+ converter.call("2012-05-15 13:42:54.000Z").should == t
95
+ converter.call("2012-05-15T13:42:54.000Z").should == t
96
+ converter.call("2012-05-15T13:42:54Z").should == t
97
+ converter.call("20120515134254000Z").should == t
98
+ end
99
+
100
+ it "does not require time part" do
101
+ converter.call("2012-05-15 13:42").should == Time.utc(2012, 5, 15, 13, 42)
102
+ converter.call("2012-05-15 13").should == Time.utc(2012, 5, 15, 13)
103
+ converter.call("2012-05-15").should == Time.utc(2012, 5, 15)
104
+ end
105
+
106
+ it "raises error if can format is wrong" do
107
+ lambda { converter.call("2/10/2031 13:44:22") }.should raise_error(InputSanitizer::ConversionError)
108
+ end
109
+
110
+ it "raises error if date is wrong" do
111
+ lambda { converter.call("2012-02-32") }.should raise_error(InputSanitizer::ConversionError)
112
+ end
113
+
114
+ it "allows the instance of Time" do
115
+ t = Time.now
116
+ converter.call(t).should == t.utc
117
+ end
118
+
119
+ it "raises error if value is of invalid type" do
120
+ lambda { converter.call({}) }.should raise_error(InputSanitizer::ConversionError)
121
+ end
122
+ end
123
+
124
+ describe InputSanitizer::V1::AllowNil do
125
+ it "passes blanks" do
126
+ lambda { |_| 1 }.extend(InputSanitizer::V1::AllowNil).call("").should be_nil
127
+ end
128
+
129
+ it "passes things the extended sanitizer passes" do
130
+ lambda { |_| :something }.extend(InputSanitizer::V1::AllowNil).call(:stuff).
131
+ should eq(:something)
132
+ end
133
+
134
+ it "raises error if the extended sanitizer raises error" do
135
+ action = lambda do
136
+ lambda { |_| raise "Some error" }.extend(InputSanitizer::V1::AllowNil).call(:stuff)
137
+ end
138
+
139
+ action.should raise_error
140
+ end
141
+ end
@@ -0,0 +1,174 @@
1
+ require 'spec_helper'
2
+
3
+ # OPTIONS
4
+ # :allow_nil (default true)
5
+ # :allow_blank (default true)
6
+ # :require (default false, requires given key in params, and additionally sets :allow_nil and :allow_blank to false)
7
+
8
+ # | allow_nil | allow_blank
9
+ # __________|___________|_____________
10
+ # string | YES | YES
11
+ # url | YES | YES
12
+ # datetime | YES | YES
13
+ # integer | YES | NO
14
+ # boolean | NO | NO
15
+ # ------------------------------------
16
+
17
+ # [type, value, option] => valid? # if valid? == false then check for ValueMissing error
18
+
19
+ test_cases = {
20
+ [InputSanitizer::V2::Types::StringCheck, { :allow_nil => false }, nil] => false,
21
+ [InputSanitizer::V2::Types::StringCheck, { :allow_nil => false }, ""] => true,
22
+ [InputSanitizer::V2::Types::StringCheck, { :allow_nil => false }, "zz"] => true,
23
+ [InputSanitizer::V2::Types::StringCheck, { :allow_blank => false }, nil] => false,
24
+ [InputSanitizer::V2::Types::StringCheck, { :allow_blank => false }, ""] => false,
25
+ [InputSanitizer::V2::Types::StringCheck, { :allow_blank => false }, "zz"] => true,
26
+ [InputSanitizer::V2::Types::StringCheck, { :required => true }, nil] => false,
27
+ [InputSanitizer::V2::Types::StringCheck, { :required => true }, ""] => false,
28
+ [InputSanitizer::V2::Types::StringCheck, { :required => true }, "zz"] => true,
29
+ [InputSanitizer::V2::Types::StringCheck, { }, nil] => true,
30
+ [InputSanitizer::V2::Types::StringCheck, { }, ""] => true,
31
+ [InputSanitizer::V2::Types::StringCheck, { }, "zz"] => true,
32
+ [InputSanitizer::V2::Types::IntegerCheck, { :allow_nil => false }, nil] => false,
33
+ [InputSanitizer::V2::Types::IntegerCheck, { :allow_nil => false }, ""] => :invalid_type,
34
+ [InputSanitizer::V2::Types::IntegerCheck, { :allow_nil => false }, 1] => true,
35
+ [InputSanitizer::V2::Types::IntegerCheck, { :allow_blank => false }, nil] => false,
36
+ [InputSanitizer::V2::Types::IntegerCheck, { :allow_blank => false }, ""] => :invalid_type,
37
+ [InputSanitizer::V2::Types::IntegerCheck, { :allow_blank => false }, 1] => true,
38
+ [InputSanitizer::V2::Types::IntegerCheck, { :required => true }, nil] => false,
39
+ [InputSanitizer::V2::Types::IntegerCheck, { :required => true }, ""] => :invalid_type,
40
+ [InputSanitizer::V2::Types::IntegerCheck, { :required => true }, 1] => true,
41
+ [InputSanitizer::V2::Types::IntegerCheck, { }, nil] => true,
42
+ [InputSanitizer::V2::Types::IntegerCheck, { }, ""] => :invalid_type,
43
+ [InputSanitizer::V2::Types::IntegerCheck, { }, 1] => true,
44
+ [InputSanitizer::V2::Types::CoercingIntegerCheck, { :allow_nil => false }, nil] => false,
45
+ [InputSanitizer::V2::Types::CoercingIntegerCheck, { :allow_nil => false }, "null"] => false,
46
+ [InputSanitizer::V2::Types::CoercingIntegerCheck, { :allow_nil => false }, ""] => :invalid_type,
47
+ [InputSanitizer::V2::Types::CoercingIntegerCheck, { :allow_nil => false }, 1] => true,
48
+ [InputSanitizer::V2::Types::CoercingIntegerCheck, { :allow_blank => false }, nil] => false,
49
+ [InputSanitizer::V2::Types::CoercingIntegerCheck, { :allow_blank => false }, "null"] => false,
50
+ [InputSanitizer::V2::Types::CoercingIntegerCheck, { :allow_blank => false }, ""] => :invalid_type,
51
+ [InputSanitizer::V2::Types::CoercingIntegerCheck, { :allow_blank => false }, 1] => true,
52
+ [InputSanitizer::V2::Types::CoercingIntegerCheck, { :required => true }, nil] => false,
53
+ [InputSanitizer::V2::Types::CoercingIntegerCheck, { :required => true }, "null"] => false,
54
+ [InputSanitizer::V2::Types::CoercingIntegerCheck, { :required => true }, ""] => :invalid_type,
55
+ [InputSanitizer::V2::Types::CoercingIntegerCheck, { :required => true }, 1] => true,
56
+ [InputSanitizer::V2::Types::CoercingIntegerCheck, { }, nil] => true,
57
+ [InputSanitizer::V2::Types::CoercingIntegerCheck, { }, "null"] => true,
58
+ [InputSanitizer::V2::Types::CoercingIntegerCheck, { }, ""] => :invalid_type,
59
+ [InputSanitizer::V2::Types::CoercingIntegerCheck, { }, 1] => true,
60
+ [InputSanitizer::V2::Types::FloatCheck, { :allow_nil => false }, nil] => false,
61
+ [InputSanitizer::V2::Types::FloatCheck, { :allow_nil => false }, ""] => :invalid_type,
62
+ [InputSanitizer::V2::Types::FloatCheck, { :allow_nil => false }, 1] => true,
63
+ [InputSanitizer::V2::Types::FloatCheck, { :allow_nil => false }, 3.1415] => true,
64
+ [InputSanitizer::V2::Types::FloatCheck, { :allow_blank => false }, nil] => false,
65
+ [InputSanitizer::V2::Types::FloatCheck, { :allow_blank => false }, ""] => :invalid_type,
66
+ [InputSanitizer::V2::Types::FloatCheck, { :allow_blank => false }, 1] => true,
67
+ [InputSanitizer::V2::Types::FloatCheck, { :allow_blank => false }, 3.1415] => true,
68
+ [InputSanitizer::V2::Types::FloatCheck, { :required => true }, nil] => false,
69
+ [InputSanitizer::V2::Types::FloatCheck, { :required => true }, ""] => :invalid_type,
70
+ [InputSanitizer::V2::Types::FloatCheck, { :required => true }, 1] => true,
71
+ [InputSanitizer::V2::Types::FloatCheck, { :required => true }, 3.1415] => true,
72
+ [InputSanitizer::V2::Types::FloatCheck, { }, nil] => true,
73
+ [InputSanitizer::V2::Types::FloatCheck, { }, ""] => :invalid_type,
74
+ [InputSanitizer::V2::Types::FloatCheck, { }, 1] => true,
75
+ [InputSanitizer::V2::Types::FloatCheck, { }, 3.1415] => true,
76
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { :allow_nil => false }, nil] => false,
77
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { :allow_nil => false }, "null"] => false,
78
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { :allow_nil => false }, ""] => :invalid_type,
79
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { :allow_nil => false }, 1] => true,
80
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { :allow_nil => false }, 3.1415] => true,
81
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { :allow_nil => false }, "3.1415"] => true,
82
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { :allow_blank => false }, nil] => false,
83
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { :allow_blank => false }, "null"] => false,
84
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { :allow_blank => false }, ""] => :invalid_type,
85
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { :allow_blank => false }, 1] => true,
86
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { :allow_blank => false }, 3.1415] => true,
87
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { :allow_blank => false }, "3.1415"] => true,
88
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { :required => true }, nil] => false,
89
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { :required => true }, "null"] => false,
90
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { :required => true }, ""] => :invalid_type,
91
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { :required => true }, 1] => true,
92
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { :required => true }, 3.1415] => true,
93
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { :required => true }, "3.1415"] => true,
94
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { }, nil] => true,
95
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { }, "null"] => true,
96
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { }, ""] => :invalid_type,
97
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { }, 1] => true,
98
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { }, 3.1415] => true,
99
+ [InputSanitizer::V2::Types::CoercingFloatCheck, { }, "3.1415"] => true,
100
+ [InputSanitizer::V2::Types::BooleanCheck, { :allow_nil => false }, nil] => false,
101
+ [InputSanitizer::V2::Types::BooleanCheck, { :allow_nil => false }, ""] => :invalid_type,
102
+ [InputSanitizer::V2::Types::BooleanCheck, { :allow_nil => false }, true] => true,
103
+ [InputSanitizer::V2::Types::BooleanCheck, { :allow_blank => false }, nil] => false,
104
+ [InputSanitizer::V2::Types::BooleanCheck, { :allow_blank => false }, ""] => :invalid_type,
105
+ [InputSanitizer::V2::Types::BooleanCheck, { :allow_blank => false }, true] => true,
106
+ [InputSanitizer::V2::Types::BooleanCheck, { :required => true }, nil] => false,
107
+ [InputSanitizer::V2::Types::BooleanCheck, { :required => true }, ""] => :invalid_type,
108
+ [InputSanitizer::V2::Types::BooleanCheck, { :required => true }, true] => true,
109
+ [InputSanitizer::V2::Types::BooleanCheck, { }, nil] => false,
110
+ [InputSanitizer::V2::Types::BooleanCheck, { }, ""] => :invalid_type,
111
+ [InputSanitizer::V2::Types::BooleanCheck, { }, true] => true,
112
+ [InputSanitizer::V2::Types::DatetimeCheck, { :allow_nil => false }, nil] => false,
113
+ [InputSanitizer::V2::Types::DatetimeCheck, { :allow_nil => false }, ""] => true,
114
+ [InputSanitizer::V2::Types::DatetimeCheck, { :allow_nil => false }, "2014-08-27T16:32:56Z"] => true,
115
+ [InputSanitizer::V2::Types::DatetimeCheck, { :allow_blank => false }, nil] => false,
116
+ [InputSanitizer::V2::Types::DatetimeCheck, { :allow_blank => false }, ""] => false,
117
+ [InputSanitizer::V2::Types::DatetimeCheck, { :allow_blank => false }, "2014-08-27T16:32:56Z"] => true,
118
+ [InputSanitizer::V2::Types::DatetimeCheck, { :required => true }, nil] => false,
119
+ [InputSanitizer::V2::Types::DatetimeCheck, { :required => true }, ""] => false,
120
+ [InputSanitizer::V2::Types::DatetimeCheck, { :required => true }, "2014-08-27T16:32:56Z"] => true,
121
+ [InputSanitizer::V2::Types::DatetimeCheck, { }, nil] => true,
122
+ [InputSanitizer::V2::Types::DatetimeCheck, { }, ""] => true,
123
+ [InputSanitizer::V2::Types::DatetimeCheck, { }, "2014-08-27T16:32:56Z"] => true,
124
+ [InputSanitizer::V2::Types::DatetimeCheck, { }, 1234] => :invalid_type,
125
+ [InputSanitizer::V2::Types::DatetimeCheck, { }, []] => :invalid_type,
126
+ [InputSanitizer::V2::Types::DatetimeCheck, { }, {}] => :invalid_type,
127
+ [InputSanitizer::V2::Types::URLCheck, { :allow_nil => false }, nil] => false,
128
+ [InputSanitizer::V2::Types::URLCheck, { :allow_nil => false }, ""] => true,
129
+ [InputSanitizer::V2::Types::URLCheck, { :allow_nil => false }, "http://getbase.com"] => true,
130
+ [InputSanitizer::V2::Types::URLCheck, { :allow_blank => false }, nil] => false,
131
+ [InputSanitizer::V2::Types::URLCheck, { :allow_blank => false }, ""] => false,
132
+ [InputSanitizer::V2::Types::URLCheck, { :allow_blank => false }, "http://getbase.com"] => true,
133
+ [InputSanitizer::V2::Types::URLCheck, { :required => true }, nil] => false,
134
+ [InputSanitizer::V2::Types::URLCheck, { :required => true }, ""] => false,
135
+ [InputSanitizer::V2::Types::URLCheck, { :required => true }, "http://getbase.com"] => true,
136
+ [InputSanitizer::V2::Types::URLCheck, { }, nil] => true,
137
+ [InputSanitizer::V2::Types::URLCheck, { }, ""] => true,
138
+ [InputSanitizer::V2::Types::URLCheck, { }, "http://getbase.com"] => true,
139
+ }
140
+
141
+ describe 'type checks' do
142
+ test_cases.each do |(test_case, result)|
143
+ describe test_case[0] do
144
+ context "with options: #{test_case[1]}" do
145
+ show_value = case test_case[2]
146
+ when nil then 'nil'
147
+ when '' then "''"
148
+ else "'#{test_case[2]}'"
149
+ end
150
+
151
+ it "correctly handles value #{show_value}" do
152
+ case result
153
+ when true
154
+ lambda { test_case[0].new.call(test_case[2], test_case[1]) }.should_not raise_error
155
+ when false
156
+ lambda { test_case[0].new.call(test_case[2], test_case[1]) }.should raise_error(InputSanitizer::BlankValueError)
157
+ when :invalid_type
158
+ lambda { test_case[0].new.call(test_case[2], test_case[1]) }.should raise_error(InputSanitizer::TypeMismatchError)
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+
166
+ # Generating test cases:
167
+
168
+ # types = ['string', 'integer', 'boolean', 'datetime', 'url']
169
+ # options = [:allow_nil_false, :allow_blank_false, :required_true, :no_params]
170
+ # values = [nil, '', lambda { |type| { 'string' => 'zz', 'integer' => 1, 'boolean' => true, 'datetime' => '2014-08-27T16:32:56Z', 'url' => 'http://getbase.com' }[type] }]
171
+ # test_cases = types.product(options).product(values).map(&:flatten).map! do |test_case|
172
+ # test_case[2] = test_case[2].call(test_case[0]) if test_case[2].is_a?(Proc); test_case
173
+ # end
174
+ # test_cases.each { |test_case| puts "#{test_case.inspect} => []," }; nil
@@ -0,0 +1,460 @@
1
+ require 'spec_helper'
2
+
3
+ class AddressSanitizer < InputSanitizer::V2::PayloadSanitizer
4
+ string :city
5
+ string :zip
6
+ end
7
+
8
+ class TagSanitizer < InputSanitizer::V2::PayloadSanitizer
9
+ integer :id
10
+ string :name
11
+ nested :addresses, :sanitizer => AddressSanitizer, :collection => true
12
+ end
13
+
14
+ class TestedPayloadSanitizer < InputSanitizer::V2::PayloadSanitizer
15
+ integer :array, :collection => true
16
+ integer :array_nil, :collection => true, :allow_nil => true
17
+ string :status, :allow => ['current', 'past']
18
+ string :status_with_empty, :allow => ['', 'current', 'past']
19
+ string :regexp_string, :regexp => /^#?([a-f0-9]{6}|[a-f0-9]{3})$/
20
+ nested :address, :sanitizer => AddressSanitizer
21
+ nested :nullable_address, :sanitizer => AddressSanitizer, :allow_nil => true
22
+ nested :tags, :sanitizer => TagSanitizer, :collection => true
23
+
24
+ float :float_attribute, :minimum => 1, :maximum => 100
25
+ integer :integer_attribute, :minimum => 1, :maximum => 100
26
+ string :string_attribute
27
+ boolean :bool_attribute
28
+ datetime :datetime_attribute
29
+ date :date_attribute, :minimum => Date.new(-2015, 01, 01), :maximum => Date.new(2015, 12, 31)
30
+
31
+ url :website
32
+ string :limited_collection, :collection => { :minimum => 1, :maximum => 2 }, :minimum => 2, :maximum => 12
33
+ string :allow_collection, :collection => true, :allow => ['yes', 'no']
34
+ end
35
+
36
+ class BlankValuesPayloadSanitizer < InputSanitizer::V2::PayloadSanitizer
37
+ string :required_string, :required => true
38
+ datetime :non_nil_datetime, :allow_nil => false
39
+ url :non_blank_url, :allow_blank => false
40
+ end
41
+
42
+ class CustomConverterWithProvidedValue < InputSanitizer::V2::PayloadSanitizer
43
+ integer :from
44
+ custom :to, :provide => :from, :converter => lambda { |value, options|
45
+ InputSanitizer::V2::Types::IntegerCheck.new.call(value)
46
+ raise InputSanitizer::ValueError.new(value, options[:provided][:from], nil) if options[:provided][:from] > value
47
+ value
48
+ }
49
+ end
50
+
51
+ describe InputSanitizer::V2::PayloadSanitizer do
52
+ let(:sanitizer) { TestedPayloadSanitizer.new(@params) }
53
+ let(:cleaned) { sanitizer.cleaned }
54
+
55
+ describe "collections" do
56
+ it "is invalid if collection is not an array" do
57
+ @params = { :array => {} }
58
+ sanitizer.should_not be_valid
59
+ end
60
+
61
+ it "is valid if collection is an array" do
62
+ @params = { :array => [] }
63
+ sanitizer.should be_valid
64
+ end
65
+
66
+ it "is valid if collection is an nil and allow_nil is passed" do
67
+ @params = { :array_nil => nil }
68
+ sanitizer.should be_valid
69
+ end
70
+
71
+ it "is invalid if there are too few elements" do
72
+ @params = { :limited_collection => [] }
73
+ sanitizer.should_not be_valid
74
+ end
75
+
76
+ it "is invalid if there are too many elements" do
77
+ @params = { :limited_collection => ['bear', 'bear', 'bear'] }
78
+ sanitizer.should_not be_valid
79
+ end
80
+
81
+ it "is valid when there are just enough elements" do
82
+ @params = { :limited_collection => ['goldilocks'] }
83
+ sanitizer.should be_valid
84
+ end
85
+
86
+ it "is invalid when any of the elements are too long" do
87
+ @params = { :limited_collection => ['more_than_the_limit'] }
88
+ sanitizer.should_not be_valid
89
+ end
90
+
91
+ it "is invalid when any of the elements are too short" do
92
+ @params = { :limited_collection => ['a'] }
93
+ sanitizer.should_not be_valid
94
+ end
95
+
96
+ it "is invalid when given disallowed value in a collection" do
97
+ @params = { :allow_collection => ['yes', 'no', 'whoa'] }
98
+ sanitizer.should_not be_valid
99
+ end
100
+
101
+ it "is valid when given allowed values in a collection" do
102
+ @params = { :allow_collection => ['yes', 'no'] }
103
+ sanitizer.should be_valid
104
+ end
105
+ end
106
+
107
+ describe "allow option" do
108
+ it "is valid when given an allowed string" do
109
+ @params = { :status => 'past' }
110
+ sanitizer.should be_valid
111
+ end
112
+
113
+ it "is invalid when given an empty string" do
114
+ @params = { :status => '' }
115
+ sanitizer.should_not be_valid
116
+ sanitizer.errors[0].field.should eq('/status')
117
+ end
118
+
119
+ it "is valid when given an allowed empty string" do
120
+ @params = { :status_with_empty => '' }
121
+ sanitizer.should be_valid
122
+ end
123
+
124
+ it "is invalid when given a disallowed string" do
125
+ @params = { :status => 'current bad string' }
126
+ sanitizer.should_not be_valid
127
+ sanitizer.errors[0].field.should eq('/status')
128
+ end
129
+ end
130
+
131
+ describe "minimum and maximum options" do
132
+ it "is invalid if integer is lower than the minimum" do
133
+ @params = { :integer_attribute => 0 }
134
+ sanitizer.should_not be_valid
135
+ end
136
+
137
+ it "is invalid if integer is greater than the maximum" do
138
+ @params = { :integer_attribute => 101 }
139
+ sanitizer.should_not be_valid
140
+ end
141
+
142
+ it "is valid when integer is within given range" do
143
+ @params = { :limited_collection => ['goldilocks'] }
144
+ sanitizer.should be_valid
145
+ end
146
+
147
+ it "is invalid if float is lower than the minimum" do
148
+ @params = { :float_attribute => 0.0 }
149
+ sanitizer.should_not be_valid
150
+ end
151
+
152
+ it "is invalid if float is greater than the maximum" do
153
+ @params = { :float_attribute => 101.0 }
154
+ sanitizer.should_not be_valid
155
+ end
156
+ end
157
+
158
+ describe "strict param checking" do
159
+ it "is invalid when given extra params" do
160
+ @params = { :extra => 'test', :extra2 => 1 }
161
+ sanitizer.should_not be_valid
162
+ sanitizer.errors.count.should eq(2)
163
+ end
164
+
165
+ it "is invalid when given extra params in a nested sanitizer" do
166
+ @params = { :address => { :extra => 0 }, :tags => [ { :extra2 => 1 } ] }
167
+ sanitizer.should_not be_valid
168
+ sanitizer.errors.map(&:field).should contain_exactly('/address/extra', '/tags/0/extra2')
169
+ end
170
+ end
171
+
172
+ describe "converters with provided values" do
173
+ let(:sanitizer) { CustomConverterWithProvidedValue.new(@params) }
174
+
175
+ it "is valid when converter passes check with provided value" do
176
+ @params = {from: 1, to: 3}
177
+ sanitizer.should be_valid
178
+ end
179
+
180
+ it "is invalid when converter does not pass check with provided value" do
181
+ @params = {from: 3, to: 1}
182
+ sanitizer.should_not be_valid
183
+ end
184
+ end
185
+
186
+ describe "strict type checking" do
187
+ it "is invalid when given string instead of integer" do
188
+ @params = { :integer_attribute => '1' }
189
+ sanitizer.should_not be_valid
190
+ sanitizer.errors[0].field.should eq('/integer_attribute')
191
+ end
192
+
193
+ it "is valid when given an integer" do
194
+ @params = { :integer_attribute => 50 }
195
+ sanitizer.should be_valid
196
+ sanitizer[:integer_attribute].should eq(50)
197
+ end
198
+
199
+ it "is invalid when given a float" do
200
+ @params = { :integer_attribute => 50.99 }
201
+ sanitizer.should_not be_valid
202
+ end
203
+
204
+ it "is valid when given nil for an integer" do
205
+ @params = { :integer_attribute => nil }
206
+ sanitizer.should be_valid
207
+ sanitizer[:integer_attribute].should be_nil
208
+ end
209
+
210
+ it "is invalid when given string instead of float" do
211
+ @params = { :float_attribute => '1' }
212
+ sanitizer.should_not be_valid
213
+ sanitizer.errors[0].field.should eq('/float_attribute')
214
+ end
215
+
216
+ it "is valid when given a float" do
217
+ @params = { :float_attribute => 50.0 }
218
+ sanitizer.should be_valid
219
+ sanitizer[:float_attribute].should eq(50.0)
220
+ end
221
+
222
+ it "is valid when given nil for a float" do
223
+ @params = { :float_attribute => nil }
224
+ sanitizer.should be_valid
225
+ sanitizer[:float_attribute].should be_nil
226
+ end
227
+
228
+ it "is valid when given string is matching regexp" do
229
+ @params = { :regexp_string => "#8bd635" }
230
+ sanitizer.should be_valid
231
+ sanitizer[:regexp_string].should eq('#8bd635')
232
+ end
233
+
234
+ it "is invalid when given string is not matching regexp" do
235
+ @params = { :regexp_string => "not a hex value" }
236
+ sanitizer.should_not be_valid
237
+ sanitizer.errors[0].field.should eq('/regexp_string')
238
+ end
239
+
240
+ it "is invalid when given integer instead of string" do
241
+ @params = { :string_attribute => 0 }
242
+ sanitizer.should_not be_valid
243
+ sanitizer.errors[0].field.should eq('/string_attribute')
244
+ end
245
+
246
+ it "is invalid when given float instead of string" do
247
+ @params = { :string_attribute => 3.1415 }
248
+ sanitizer.should_not be_valid
249
+ sanitizer.errors[0].field.should eq('/string_attribute')
250
+ end
251
+
252
+ it "is valid when given a string" do
253
+ @params = { :string_attribute => '#@!#%#$@#ad' }
254
+ sanitizer.should be_valid
255
+ sanitizer[:string_attribute].should eq('#@!#%#$@#ad')
256
+ end
257
+
258
+ it "is invalid when given 'yes' as a bool" do
259
+ @params = { :bool_attribute => 'yes' }
260
+ sanitizer.should_not be_valid
261
+ sanitizer.errors[0].field.should eq('/bool_attribute')
262
+ end
263
+
264
+ it "is valid when given true as a bool" do
265
+ @params = { :bool_attribute => true }
266
+ sanitizer.should be_valid
267
+ end
268
+
269
+ it "is valid when given false as a bool" do
270
+ @params = { :bool_attribute => false }
271
+ sanitizer.should be_valid
272
+ end
273
+
274
+ it "is invalid when given an incorrect datetime" do
275
+ @params = { :datetime_attribute => "2014-08-2716:32:56Z" }
276
+ sanitizer.should_not be_valid
277
+ sanitizer.errors[0].field.should eq('/datetime_attribute')
278
+ end
279
+
280
+ it "is valid when given a correct datetime" do
281
+ @params = { :datetime_attribute => "2014-08-27T16:32:56Z" }
282
+ sanitizer.should be_valid
283
+ end
284
+
285
+ it "is valid when given a 'forever' timestamp" do
286
+ @params = { :datetime_attribute => "9999-12-31T00:00:00Z" }
287
+ sanitizer.should be_valid
288
+ end
289
+
290
+ it "is invalid when given an incorrect date" do
291
+ @params = { :date_attribute => "invalid" }
292
+ sanitizer.should_not be_valid
293
+ sanitizer.errors[0].field.should eq('/date_attribute')
294
+ end
295
+
296
+ it "is valid when given a correct date" do
297
+ @params = { :date_attribute => "2015-08-27" }
298
+ sanitizer.should be_valid
299
+ end
300
+
301
+ it "is valid when given a correct negative date" do
302
+ @params = { :date_attribute => "-2014-08-27" }
303
+ sanitizer.should be_valid
304
+ end
305
+
306
+ it "is valid when given a correct URL" do
307
+ @params = { :website => "https://google.com" }
308
+ sanitizer.should be_valid
309
+ sanitizer[:website].should eq("https://google.com")
310
+ end
311
+
312
+ it "is invalid when given an invalid URL" do
313
+ @params = { :website => "ht:/google.com" }
314
+ sanitizer.should_not be_valid
315
+ end
316
+
317
+ it "is invalid when given an invalid URL that contains a valid URL" do
318
+ @params = { :website => "watwat http://google.com wat" }
319
+ sanitizer.should_not be_valid
320
+ end
321
+
322
+ describe "blank and required values" do
323
+ let(:sanitizer) { BlankValuesPayloadSanitizer.new(@params) }
324
+ let(:defaults) { { :required_string => 'zz' } }
325
+
326
+ it "is invalid if required string is missing" do
327
+ @params = {}
328
+ sanitizer.should_not be_valid
329
+ sanitizer.errors[0].should be_an_instance_of(InputSanitizer::ValueMissingError)
330
+ sanitizer.errors[0].field.should eq('/required_string')
331
+ end
332
+
333
+ it "is invalid if required string is nil" do
334
+ @params = { :required_string => nil }
335
+ sanitizer.should_not be_valid
336
+ sanitizer.errors[0].should be_an_instance_of(InputSanitizer::BlankValueError)
337
+ sanitizer.errors[0].field.should eq('/required_string')
338
+ end
339
+
340
+ it "is invalid if required string is blank" do
341
+ @params = { :required_string => ' ' }
342
+ sanitizer.should_not be_valid
343
+ sanitizer.errors[0].should be_an_instance_of(InputSanitizer::BlankValueError)
344
+ sanitizer.errors[0].field.should eq('/required_string')
345
+ end
346
+
347
+ it "is invalid if non-nil datetime is null" do
348
+ @params = defaults.merge({ :non_nil_datetime => nil })
349
+ sanitizer.should_not be_valid
350
+ sanitizer.errors[0].should be_an_instance_of(InputSanitizer::BlankValueError)
351
+ sanitizer.errors[0].field.should eq('/non_nil_datetime')
352
+ end
353
+
354
+ it "is valid if non-nil datetime is blank" do
355
+ @params = defaults.merge({ :non_nil_datetime => '' })
356
+ sanitizer.should be_valid
357
+ end
358
+
359
+ it "is invalid if non-blank url is nil" do
360
+ @params = defaults.merge({ :non_blank_url => nil })
361
+ sanitizer.should_not be_valid
362
+ sanitizer.errors[0].should be_an_instance_of(InputSanitizer::BlankValueError)
363
+ sanitizer.errors[0].field.should eq('/non_blank_url')
364
+ end
365
+
366
+ it "is invalid if non-blank url is blank" do
367
+ @params = defaults.merge({ :non_blank_url => '' })
368
+ sanitizer.should_not be_valid
369
+ sanitizer.errors[0].should be_an_instance_of(InputSanitizer::BlankValueError)
370
+ sanitizer.errors[0].field.should eq('/non_blank_url')
371
+ end
372
+ end
373
+
374
+ describe "nested checking" do
375
+ describe "simple array" do
376
+ it "returns JSON pointer for invalid fields" do
377
+ @params = { :array => [1, 'z', '3', 4] }
378
+ sanitizer.errors.length.should eq(2)
379
+ sanitizer.errors.map(&:field).should contain_exactly('/array/1', '/array/2')
380
+ end
381
+ end
382
+
383
+ describe "nested object" do
384
+ it "returns an error when given a nil for a nested value" do
385
+ @params = { :address => nil }
386
+ sanitizer.should_not be_valid
387
+ end
388
+
389
+ it "returns an error when given a string for a nested value" do
390
+ @params = { :address => 'nope' }
391
+ sanitizer.should_not be_valid
392
+ end
393
+
394
+ it "returns an error when given an array for a nested value" do
395
+ @params = { :address => ['a'] }
396
+ sanitizer.should_not be_valid
397
+ end
398
+
399
+ it "returns JSON pointer for invalid fields" do
400
+ @params = { :address => { :city => 0, :zip => 1 } }
401
+ sanitizer.errors.length.should eq(2)
402
+ sanitizer.errors.map(&:field).should contain_exactly('/address/city', '/address/zip')
403
+ end
404
+
405
+ it "allows nil with `allow_nil` flag" do
406
+ @params = { :nullable_address => nil }
407
+ sanitizer.should be_valid
408
+ sanitizer.cleaned.fetch(:nullable_address).should eq(nil)
409
+ end
410
+ end
411
+
412
+ describe "array of nested objects" do
413
+ it "returns an error when given a nil for a collection" do
414
+ @params = { :tags => nil }
415
+ sanitizer.should_not be_valid
416
+ end
417
+
418
+ it "returns an error when given a string for a collection" do
419
+ @params = { :tags => 'nope' }
420
+ sanitizer.should_not be_valid
421
+ end
422
+
423
+ it "returns an error when given a hash for a collection" do
424
+ @params = { :tags => { :a => 1 } }
425
+ sanitizer.should_not be_valid
426
+ end
427
+
428
+ it "returns JSON pointer for invalid fields" do
429
+ @params = { :tags => [ { :id => 'n', :name => 1 }, { :id => 10, :name => 2 } ] }
430
+ sanitizer.errors.length.should eq(3)
431
+ sanitizer.errors.map(&:field).should contain_exactly(
432
+ '/tags/0/id',
433
+ '/tags/0/name',
434
+ '/tags/1/name'
435
+ )
436
+ end
437
+ end
438
+
439
+ describe "array of nested objects that have array of nested objects" do
440
+ it "returns JSON pointer for invalid fields" do
441
+ @params = { :tags => [
442
+ { :id => 'n', :addresses => [ { :city => 0 }, { :city => 1 } ] },
443
+ { :name => 2, :addresses => [ { :city => 3 } ] },
444
+ ] }
445
+ sanitizer.errors.length.should eq(5)
446
+ sanitizer.errors.map(&:field).should contain_exactly(
447
+ '/tags/0/id',
448
+ '/tags/0/addresses/0/city',
449
+ '/tags/0/addresses/1/city',
450
+ '/tags/1/name',
451
+ '/tags/1/addresses/0/city'
452
+ )
453
+
454
+ ec = sanitizer.error_collection
455
+ ec.length.should eq(5)
456
+ end
457
+ end
458
+ end
459
+ end
460
+ end