hash_validator 1.2.0 → 2.0.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +173 -12
  3. data/lib/hash_validator/configuration.rb +16 -0
  4. data/lib/hash_validator/validators/alpha_validator.rb +4 -12
  5. data/lib/hash_validator/validators/alphanumeric_validator.rb +4 -12
  6. data/lib/hash_validator/validators/array_validator.rb +1 -1
  7. data/lib/hash_validator/validators/base.rb +28 -3
  8. data/lib/hash_validator/validators/boolean_validator.rb +3 -5
  9. data/lib/hash_validator/validators/class_validator.rb +1 -1
  10. data/lib/hash_validator/validators/digits_validator.rb +4 -12
  11. data/lib/hash_validator/validators/dynamic_func_validator.rb +26 -0
  12. data/lib/hash_validator/validators/dynamic_pattern_validator.rb +23 -0
  13. data/lib/hash_validator/validators/email_validator.rb +4 -6
  14. data/lib/hash_validator/validators/enumerable_validator.rb +4 -6
  15. data/lib/hash_validator/validators/hash_validator.rb +2 -2
  16. data/lib/hash_validator/validators/hex_color_validator.rb +4 -12
  17. data/lib/hash_validator/validators/ip_validator.rb +22 -0
  18. data/lib/hash_validator/validators/ipv4_validator.rb +18 -0
  19. data/lib/hash_validator/validators/ipv6_validator.rb +22 -0
  20. data/lib/hash_validator/validators/json_validator.rb +4 -11
  21. data/lib/hash_validator/validators/lambda_validator.rb +5 -8
  22. data/lib/hash_validator/validators/many_validator.rb +3 -3
  23. data/lib/hash_validator/validators/multiple_validator.rb +1 -1
  24. data/lib/hash_validator/validators/optional_validator.rb +1 -1
  25. data/lib/hash_validator/validators/presence_validator.rb +4 -6
  26. data/lib/hash_validator/validators/regex_validator.rb +4 -6
  27. data/lib/hash_validator/validators/simple_type_validators.rb +1 -1
  28. data/lib/hash_validator/validators/simple_validator.rb +2 -4
  29. data/lib/hash_validator/validators/url_validator.rb +4 -11
  30. data/lib/hash_validator/validators.rb +40 -4
  31. data/lib/hash_validator/version.rb +1 -1
  32. data/lib/hash_validator.rb +1 -0
  33. data/spec/configuration_spec.rb +189 -0
  34. data/spec/hash_validator_spec.rb +4 -4
  35. data/spec/validators/base_spec.rb +2 -2
  36. data/spec/validators/dynamic_func_validator_spec.rb +252 -0
  37. data/spec/validators/dynamic_pattern_validator_spec.rb +150 -0
  38. data/spec/validators/ip_validator_spec.rb +105 -0
  39. data/spec/validators/ipv4_validator_spec.rb +99 -0
  40. data/spec/validators/ipv6_validator_spec.rb +99 -0
  41. data/spec/validators/user_defined_spec.rb +2 -2
  42. metadata +20 -3
  43. data/Plan.md +0 -309
@@ -0,0 +1,252 @@
1
+ require 'spec_helper'
2
+
3
+ describe HashValidator::Validator::DynamicFuncValidator do
4
+ describe 'Function-based validator' do
5
+ let(:odd_func_validator) do
6
+ HashValidator::Validator::DynamicFuncValidator.new(
7
+ 'odd_func',
8
+ ->(n) { n.is_a?(Integer) && n.odd? },
9
+ 'is not an odd integer'
10
+ )
11
+ end
12
+
13
+ let(:range_validator) do
14
+ HashValidator::Validator::DynamicFuncValidator.new(
15
+ 'in_range',
16
+ ->(n) { n.is_a?(Numeric) && n >= 18 && n <= 65 },
17
+ 'must be between 18 and 65'
18
+ )
19
+ end
20
+
21
+ let(:custom_validator) do
22
+ HashValidator::Validator::DynamicFuncValidator.new(
23
+ 'custom',
24
+ proc { |v| v.to_s.length > 5 },
25
+ 'must be longer than 5 characters'
26
+ )
27
+ end
28
+
29
+ let(:errors) { Hash.new }
30
+
31
+ describe '#initialize' do
32
+ it 'requires a callable object' do
33
+ expect {
34
+ HashValidator::Validator::DynamicFuncValidator.new('test', 'not_callable')
35
+ }.to raise_error(ArgumentError, 'Function must be callable (proc or lambda)')
36
+ end
37
+
38
+ it 'accepts a lambda' do
39
+ validator = HashValidator::Validator::DynamicFuncValidator.new(
40
+ 'test',
41
+ ->(v) { true }
42
+ )
43
+ expect(validator.func).to respond_to(:call)
44
+ end
45
+
46
+ it 'accepts a proc' do
47
+ validator = HashValidator::Validator::DynamicFuncValidator.new(
48
+ 'test',
49
+ proc { |v| true }
50
+ )
51
+ expect(validator.func).to respond_to(:call)
52
+ end
53
+
54
+ it 'accepts a custom error message' do
55
+ validator = HashValidator::Validator::DynamicFuncValidator.new(
56
+ 'test',
57
+ ->(v) { true },
58
+ 'custom error'
59
+ )
60
+ expect(validator.error_message).to eq('custom error')
61
+ end
62
+
63
+ it 'uses default error message when none provided' do
64
+ validator = HashValidator::Validator::DynamicFuncValidator.new(
65
+ 'test',
66
+ ->(v) { true }
67
+ )
68
+ expect(validator.error_message).to eq('test required')
69
+ end
70
+ end
71
+
72
+ describe '#should_validate?' do
73
+ it 'validates the correct name' do
74
+ expect(odd_func_validator.should_validate?('odd_func')).to eq true
75
+ end
76
+
77
+ it 'does not validate other names' do
78
+ expect(odd_func_validator.should_validate?('even_func')).to eq false
79
+ expect(odd_func_validator.should_validate?('string')).to eq false
80
+ end
81
+ end
82
+
83
+ describe '#validate' do
84
+ context 'with odd number function' do
85
+ it 'validates odd integers correctly' do
86
+ odd_func_validator.validate(:key, 1, {}, errors)
87
+ expect(errors).to be_empty
88
+
89
+ errors.clear
90
+ odd_func_validator.validate(:key, 13, {}, errors)
91
+ expect(errors).to be_empty
92
+
93
+ errors.clear
94
+ odd_func_validator.validate(:key, -7, {}, errors)
95
+ expect(errors).to be_empty
96
+ end
97
+
98
+ it 'rejects even integers' do
99
+ odd_func_validator.validate(:key, 2, {}, errors)
100
+ expect(errors).to eq({ key: 'is not an odd integer' })
101
+
102
+ errors.clear
103
+ odd_func_validator.validate(:key, 100, {}, errors)
104
+ expect(errors).to eq({ key: 'is not an odd integer' })
105
+ end
106
+
107
+ it 'rejects non-integers' do
108
+ odd_func_validator.validate(:key, 1.5, {}, errors)
109
+ expect(errors).to eq({ key: 'is not an odd integer' })
110
+
111
+ errors.clear
112
+ odd_func_validator.validate(:key, '1', {}, errors)
113
+ expect(errors).to eq({ key: 'is not an odd integer' })
114
+ end
115
+ end
116
+
117
+ context 'with range validator' do
118
+ it 'validates numbers in range' do
119
+ range_validator.validate(:key, 18, {}, errors)
120
+ expect(errors).to be_empty
121
+
122
+ errors.clear
123
+ range_validator.validate(:key, 30, {}, errors)
124
+ expect(errors).to be_empty
125
+
126
+ errors.clear
127
+ range_validator.validate(:key, 65, {}, errors)
128
+ expect(errors).to be_empty
129
+
130
+ errors.clear
131
+ range_validator.validate(:key, 25.5, {}, errors)
132
+ expect(errors).to be_empty
133
+ end
134
+
135
+ it 'rejects numbers outside range' do
136
+ range_validator.validate(:key, 17, {}, errors)
137
+ expect(errors).to eq({ key: 'must be between 18 and 65' })
138
+
139
+ errors.clear
140
+ range_validator.validate(:key, 66, {}, errors)
141
+ expect(errors).to eq({ key: 'must be between 18 and 65' })
142
+
143
+ errors.clear
144
+ range_validator.validate(:key, -5, {}, errors)
145
+ expect(errors).to eq({ key: 'must be between 18 and 65' })
146
+ end
147
+
148
+ it 'rejects non-numeric values' do
149
+ range_validator.validate(:key, 'twenty', {}, errors)
150
+ expect(errors).to eq({ key: 'must be between 18 and 65' })
151
+ end
152
+ end
153
+
154
+ context 'with custom validator using proc' do
155
+ it 'validates strings longer than 5 chars' do
156
+ custom_validator.validate(:key, 'hello world', {}, errors)
157
+ expect(errors).to be_empty
158
+
159
+ errors.clear
160
+ custom_validator.validate(:key, 'testing', {}, errors)
161
+ expect(errors).to be_empty
162
+ end
163
+
164
+ it 'rejects strings 5 chars or shorter' do
165
+ custom_validator.validate(:key, 'hello', {}, errors)
166
+ expect(errors).to eq({ key: 'must be longer than 5 characters' })
167
+
168
+ errors.clear
169
+ custom_validator.validate(:key, 'hi', {}, errors)
170
+ expect(errors).to eq({ key: 'must be longer than 5 characters' })
171
+ end
172
+
173
+ it 'converts values to strings' do
174
+ custom_validator.validate(:key, 123456, {}, errors)
175
+ expect(errors).to be_empty
176
+
177
+ errors.clear
178
+ custom_validator.validate(:key, 12345, {}, errors)
179
+ expect(errors).to eq({ key: 'must be longer than 5 characters' })
180
+ end
181
+ end
182
+
183
+ context 'with function that raises errors' do
184
+ let(:error_func_validator) do
185
+ HashValidator::Validator::DynamicFuncValidator.new(
186
+ 'error_func',
187
+ ->(v) { raise 'intentional error' },
188
+ 'validation failed'
189
+ )
190
+ end
191
+
192
+ it 'treats exceptions as validation failures' do
193
+ error_func_validator.validate(:key, 'any value', {}, errors)
194
+ expect(errors).to eq({ key: 'validation failed' })
195
+ end
196
+ end
197
+ end
198
+
199
+ describe 'Integration with HashValidator.add_validator' do
200
+ before do
201
+ HashValidator.add_validator('adult_age',
202
+ func: ->(age) { age.is_a?(Integer) && age >= 18 },
203
+ error_message: 'must be 18 or older')
204
+
205
+ HashValidator.add_validator('palindrome',
206
+ func: proc { |s| s.to_s == s.to_s.reverse },
207
+ error_message: 'must be a palindrome')
208
+ end
209
+
210
+ after do
211
+ HashValidator.remove_validator('adult_age')
212
+ HashValidator.remove_validator('palindrome')
213
+ end
214
+
215
+ it 'can register lambda-based validators' do
216
+ validator = HashValidator.validate(
217
+ { age: 25 },
218
+ { age: 'adult_age' }
219
+ )
220
+ expect(validator.valid?).to eq true
221
+ expect(validator.errors).to be_empty
222
+ end
223
+
224
+ it 'returns errors for invalid lambda validations' do
225
+ validator = HashValidator.validate(
226
+ { age: 16 },
227
+ { age: 'adult_age' }
228
+ )
229
+ expect(validator.valid?).to eq false
230
+ expect(validator.errors).to eq({ age: 'must be 18 or older' })
231
+ end
232
+
233
+ it 'can register proc-based validators' do
234
+ validator = HashValidator.validate(
235
+ { word: 'racecar' },
236
+ { word: 'palindrome' }
237
+ )
238
+ expect(validator.valid?).to eq true
239
+ expect(validator.errors).to be_empty
240
+ end
241
+
242
+ it 'returns errors for invalid proc validations' do
243
+ validator = HashValidator.validate(
244
+ { word: 'hello' },
245
+ { word: 'palindrome' }
246
+ )
247
+ expect(validator.valid?).to eq false
248
+ expect(validator.errors).to eq({ word: 'must be a palindrome' })
249
+ end
250
+ end
251
+ end
252
+ end
@@ -0,0 +1,150 @@
1
+ require 'spec_helper'
2
+
3
+ describe HashValidator::Validator::DynamicPatternValidator do
4
+ describe 'Pattern-based validator' do
5
+ let(:odd_pattern_validator) do
6
+ HashValidator::Validator::DynamicPatternValidator.new(
7
+ 'odd_pattern',
8
+ /\A\d*[13579]\z/,
9
+ 'is not an odd number'
10
+ )
11
+ end
12
+
13
+ let(:postal_code_validator) do
14
+ HashValidator::Validator::DynamicPatternValidator.new(
15
+ 'us_postal',
16
+ /\A\d{5}(-\d{4})?\z/,
17
+ 'is not a valid US postal code'
18
+ )
19
+ end
20
+
21
+ let(:errors) { Hash.new }
22
+
23
+ describe '#initialize' do
24
+ it 'requires a valid regular expression' do
25
+ expect {
26
+ HashValidator::Validator::DynamicPatternValidator.new('test', 'not_a_regex')
27
+ }.to raise_error(ArgumentError, 'Pattern must be a regular expression')
28
+ end
29
+
30
+ it 'accepts a custom error message' do
31
+ validator = HashValidator::Validator::DynamicPatternValidator.new(
32
+ 'test',
33
+ /test/,
34
+ 'custom error'
35
+ )
36
+ expect(validator.error_message).to eq('custom error')
37
+ end
38
+
39
+ it 'uses default error message when none provided' do
40
+ validator = HashValidator::Validator::DynamicPatternValidator.new('test', /test/)
41
+ expect(validator.error_message).to eq('test required')
42
+ end
43
+ end
44
+
45
+ describe '#should_validate?' do
46
+ it 'validates the correct name' do
47
+ expect(odd_pattern_validator.should_validate?('odd_pattern')).to eq true
48
+ end
49
+
50
+ it 'does not validate other names' do
51
+ expect(odd_pattern_validator.should_validate?('even_pattern')).to eq false
52
+ expect(odd_pattern_validator.should_validate?('string')).to eq false
53
+ end
54
+ end
55
+
56
+ describe '#validate' do
57
+ context 'with odd number pattern' do
58
+ it 'validates odd numbers correctly' do
59
+ odd_pattern_validator.validate(:key, '1', {}, errors)
60
+ expect(errors).to be_empty
61
+
62
+ errors.clear
63
+ odd_pattern_validator.validate(:key, '13', {}, errors)
64
+ expect(errors).to be_empty
65
+
66
+ errors.clear
67
+ odd_pattern_validator.validate(:key, '999', {}, errors)
68
+ expect(errors).to be_empty
69
+ end
70
+
71
+ it 'rejects even numbers' do
72
+ odd_pattern_validator.validate(:key, '2', {}, errors)
73
+ expect(errors).to eq({ key: 'is not an odd number' })
74
+
75
+ errors.clear
76
+ odd_pattern_validator.validate(:key, '100', {}, errors)
77
+ expect(errors).to eq({ key: 'is not an odd number' })
78
+ end
79
+
80
+ it 'rejects non-numeric strings' do
81
+ odd_pattern_validator.validate(:key, 'abc', {}, errors)
82
+ expect(errors).to eq({ key: 'is not an odd number' })
83
+ end
84
+
85
+ it 'converts non-string values to strings' do
86
+ odd_pattern_validator.validate(:key, 13, {}, errors)
87
+ expect(errors).to be_empty
88
+
89
+ errors.clear
90
+ odd_pattern_validator.validate(:key, 14, {}, errors)
91
+ expect(errors).to eq({ key: 'is not an odd number' })
92
+ end
93
+ end
94
+
95
+ context 'with postal code pattern' do
96
+ it 'validates correct postal codes' do
97
+ postal_code_validator.validate(:key, '12345', {}, errors)
98
+ expect(errors).to be_empty
99
+
100
+ errors.clear
101
+ postal_code_validator.validate(:key, '12345-6789', {}, errors)
102
+ expect(errors).to be_empty
103
+ end
104
+
105
+ it 'rejects invalid postal codes' do
106
+ postal_code_validator.validate(:key, '1234', {}, errors)
107
+ expect(errors).to eq({ key: 'is not a valid US postal code' })
108
+
109
+ errors.clear
110
+ postal_code_validator.validate(:key, '12345-678', {}, errors)
111
+ expect(errors).to eq({ key: 'is not a valid US postal code' })
112
+
113
+ errors.clear
114
+ postal_code_validator.validate(:key, 'ABCDE', {}, errors)
115
+ expect(errors).to eq({ key: 'is not a valid US postal code' })
116
+ end
117
+ end
118
+ end
119
+
120
+ describe 'Integration with HashValidator.add_validator' do
121
+ before do
122
+ HashValidator.add_validator('phone_number',
123
+ pattern: /\A\d{3}-\d{3}-\d{4}\z/,
124
+ error_message: 'is not a valid phone number')
125
+ end
126
+
127
+ after do
128
+ HashValidator.remove_validator('phone_number')
129
+ end
130
+
131
+ it 'can be registered and used via add_validator' do
132
+ validator = HashValidator.validate(
133
+ { phone: '555-123-4567' },
134
+ { phone: 'phone_number' }
135
+ )
136
+ expect(validator.valid?).to eq true
137
+ expect(validator.errors).to be_empty
138
+ end
139
+
140
+ it 'returns errors for invalid values' do
141
+ validator = HashValidator.validate(
142
+ { phone: '5551234567' },
143
+ { phone: 'phone_number' }
144
+ )
145
+ expect(validator.valid?).to eq false
146
+ expect(validator.errors).to eq({ phone: 'is not a valid phone number' })
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,105 @@
1
+ require 'spec_helper'
2
+
3
+ describe HashValidator::Validator::IpValidator do
4
+ let(:validator) { HashValidator::Validator::IpValidator.new }
5
+
6
+ context 'valid IP addresses' do
7
+ it 'validates IPv4 addresses' do
8
+ errors = {}
9
+ validator.validate('key', '192.168.1.1', {}, errors)
10
+ expect(errors).to be_empty
11
+ end
12
+
13
+ it 'validates IPv4 localhost' do
14
+ errors = {}
15
+ validator.validate('key', '127.0.0.1', {}, errors)
16
+ expect(errors).to be_empty
17
+ end
18
+
19
+ it 'validates IPv4 zero address' do
20
+ errors = {}
21
+ validator.validate('key', '0.0.0.0', {}, errors)
22
+ expect(errors).to be_empty
23
+ end
24
+
25
+ it 'validates IPv4 broadcast address' do
26
+ errors = {}
27
+ validator.validate('key', '255.255.255.255', {}, errors)
28
+ expect(errors).to be_empty
29
+ end
30
+
31
+ it 'validates IPv6 addresses' do
32
+ errors = {}
33
+ validator.validate('key', '2001:db8:85a3::8a2e:370:7334', {}, errors)
34
+ expect(errors).to be_empty
35
+ end
36
+
37
+ it 'validates IPv6 localhost' do
38
+ errors = {}
39
+ validator.validate('key', '::1', {}, errors)
40
+ expect(errors).to be_empty
41
+ end
42
+
43
+ it 'validates IPv6 zero address' do
44
+ errors = {}
45
+ validator.validate('key', '::', {}, errors)
46
+ expect(errors).to be_empty
47
+ end
48
+
49
+ it 'validates IPv4-mapped IPv6 addresses' do
50
+ errors = {}
51
+ validator.validate('key', '::ffff:192.168.1.1', {}, errors)
52
+ expect(errors).to be_empty
53
+ end
54
+ end
55
+
56
+ context 'invalid IP addresses' do
57
+ it 'rejects non-string values' do
58
+ errors = {}
59
+ validator.validate('key', 123, {}, errors)
60
+ expect(errors['key']).to eq('is not a valid IP address')
61
+ end
62
+
63
+ it 'rejects nil values' do
64
+ errors = {}
65
+ validator.validate('key', nil, {}, errors)
66
+ expect(errors['key']).to eq('is not a valid IP address')
67
+ end
68
+
69
+ it 'rejects malformed IPv4 addresses' do
70
+ errors = {}
71
+ validator.validate('key', '256.1.1.1', {}, errors)
72
+ expect(errors['key']).to eq('is not a valid IP address')
73
+ end
74
+
75
+ it 'rejects malformed IPv6 addresses' do
76
+ errors = {}
77
+ validator.validate('key', '2001:db8:85a3::8a2e::7334', {}, errors)
78
+ expect(errors['key']).to eq('is not a valid IP address')
79
+ end
80
+
81
+ it 'rejects non-IP strings' do
82
+ errors = {}
83
+ validator.validate('key', 'not an ip', {}, errors)
84
+ expect(errors['key']).to eq('is not a valid IP address')
85
+ end
86
+
87
+ it 'rejects empty strings' do
88
+ errors = {}
89
+ validator.validate('key', '', {}, errors)
90
+ expect(errors['key']).to eq('is not a valid IP address')
91
+ end
92
+
93
+ it 'rejects hostnames' do
94
+ errors = {}
95
+ validator.validate('key', 'example.com', {}, errors)
96
+ expect(errors['key']).to eq('is not a valid IP address')
97
+ end
98
+
99
+ it 'rejects URLs' do
100
+ errors = {}
101
+ validator.validate('key', 'http://192.168.1.1', {}, errors)
102
+ expect(errors['key']).to eq('is not a valid IP address')
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ describe HashValidator::Validator::Ipv4Validator do
4
+ let(:validator) { HashValidator::Validator::Ipv4Validator.new }
5
+
6
+ context 'valid IPv4 addresses' do
7
+ it 'validates standard IPv4 addresses' do
8
+ errors = {}
9
+ validator.validate('key', '192.168.1.1', {}, errors)
10
+ expect(errors).to be_empty
11
+ end
12
+
13
+ it 'validates localhost' do
14
+ errors = {}
15
+ validator.validate('key', '127.0.0.1', {}, errors)
16
+ expect(errors).to be_empty
17
+ end
18
+
19
+ it 'validates zero address' do
20
+ errors = {}
21
+ validator.validate('key', '0.0.0.0', {}, errors)
22
+ expect(errors).to be_empty
23
+ end
24
+
25
+ it 'validates broadcast address' do
26
+ errors = {}
27
+ validator.validate('key', '255.255.255.255', {}, errors)
28
+ expect(errors).to be_empty
29
+ end
30
+
31
+ it 'validates private network addresses' do
32
+ errors = {}
33
+ validator.validate('key', '10.0.0.1', {}, errors)
34
+ expect(errors).to be_empty
35
+ end
36
+
37
+ it 'validates Class B private addresses' do
38
+ errors = {}
39
+ validator.validate('key', '172.16.0.1', {}, errors)
40
+ expect(errors).to be_empty
41
+ end
42
+ end
43
+
44
+ context 'invalid IPv4 addresses' do
45
+ it 'rejects non-string values' do
46
+ errors = {}
47
+ validator.validate('key', 123, {}, errors)
48
+ expect(errors['key']).to eq('is not a valid IPv4 address')
49
+ end
50
+
51
+ it 'rejects nil values' do
52
+ errors = {}
53
+ validator.validate('key', nil, {}, errors)
54
+ expect(errors['key']).to eq('is not a valid IPv4 address')
55
+ end
56
+
57
+ it 'rejects octets over 255' do
58
+ errors = {}
59
+ validator.validate('key', '256.1.1.1', {}, errors)
60
+ expect(errors['key']).to eq('is not a valid IPv4 address')
61
+ end
62
+
63
+ it 'rejects too few octets' do
64
+ errors = {}
65
+ validator.validate('key', '192.168.1', {}, errors)
66
+ expect(errors['key']).to eq('is not a valid IPv4 address')
67
+ end
68
+
69
+ it 'rejects too many octets' do
70
+ errors = {}
71
+ validator.validate('key', '192.168.1.1.1', {}, errors)
72
+ expect(errors['key']).to eq('is not a valid IPv4 address')
73
+ end
74
+
75
+ it 'rejects IPv6 addresses' do
76
+ errors = {}
77
+ validator.validate('key', '2001:db8::1', {}, errors)
78
+ expect(errors['key']).to eq('is not a valid IPv4 address')
79
+ end
80
+
81
+ it 'rejects malformed addresses' do
82
+ errors = {}
83
+ validator.validate('key', '192.168.1.', {}, errors)
84
+ expect(errors['key']).to eq('is not a valid IPv4 address')
85
+ end
86
+
87
+ it 'rejects empty strings' do
88
+ errors = {}
89
+ validator.validate('key', '', {}, errors)
90
+ expect(errors['key']).to eq('is not a valid IPv4 address')
91
+ end
92
+
93
+ it 'rejects non-numeric octets' do
94
+ errors = {}
95
+ validator.validate('key', '192.168.a.1', {}, errors)
96
+ expect(errors['key']).to eq('is not a valid IPv4 address')
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ describe HashValidator::Validator::Ipv6Validator do
4
+ let(:validator) { HashValidator::Validator::Ipv6Validator.new }
5
+
6
+ context 'valid IPv6 addresses' do
7
+ it 'validates full IPv6 addresses' do
8
+ errors = {}
9
+ validator.validate('key', '2001:0db8:85a3:0000:0000:8a2e:0370:7334', {}, errors)
10
+ expect(errors).to be_empty
11
+ end
12
+
13
+ it 'validates compressed IPv6 addresses' do
14
+ errors = {}
15
+ validator.validate('key', '2001:db8:85a3::8a2e:370:7334', {}, errors)
16
+ expect(errors).to be_empty
17
+ end
18
+
19
+ it 'validates IPv6 localhost' do
20
+ errors = {}
21
+ validator.validate('key', '::1', {}, errors)
22
+ expect(errors).to be_empty
23
+ end
24
+
25
+ it 'validates IPv6 zero address' do
26
+ errors = {}
27
+ validator.validate('key', '::', {}, errors)
28
+ expect(errors).to be_empty
29
+ end
30
+
31
+ it 'validates IPv6 with leading zeros' do
32
+ errors = {}
33
+ validator.validate('key', '2001:0db8::0001', {}, errors)
34
+ expect(errors).to be_empty
35
+ end
36
+
37
+ it 'validates IPv6 with mixed case' do
38
+ errors = {}
39
+ validator.validate('key', '2001:DB8::1', {}, errors)
40
+ expect(errors).to be_empty
41
+ end
42
+
43
+ it 'validates IPv4-mapped IPv6 addresses' do
44
+ errors = {}
45
+ validator.validate('key', '::ffff:192.168.1.1', {}, errors)
46
+ expect(errors).to be_empty
47
+ end
48
+ end
49
+
50
+ context 'invalid IPv6 addresses' do
51
+ it 'rejects non-string values' do
52
+ errors = {}
53
+ validator.validate('key', 123, {}, errors)
54
+ expect(errors['key']).to eq('is not a valid IPv6 address')
55
+ end
56
+
57
+ it 'rejects nil values' do
58
+ errors = {}
59
+ validator.validate('key', nil, {}, errors)
60
+ expect(errors['key']).to eq('is not a valid IPv6 address')
61
+ end
62
+
63
+ it 'rejects IPv4 addresses' do
64
+ errors = {}
65
+ validator.validate('key', '192.168.1.1', {}, errors)
66
+ expect(errors['key']).to eq('is not a valid IPv6 address')
67
+ end
68
+
69
+ it 'rejects malformed IPv6 addresses' do
70
+ errors = {}
71
+ validator.validate('key', '2001:db8:85a3::8a2e::7334', {}, errors)
72
+ expect(errors['key']).to eq('is not a valid IPv6 address')
73
+ end
74
+
75
+ it 'rejects too many groups' do
76
+ errors = {}
77
+ validator.validate('key', '2001:0db8:85a3:0000:0000:8a2e:0370:7334:extra', {}, errors)
78
+ expect(errors['key']).to eq('is not a valid IPv6 address')
79
+ end
80
+
81
+ it 'rejects invalid characters' do
82
+ errors = {}
83
+ validator.validate('key', '2001:db8:85a3::8a2g:370:7334', {}, errors)
84
+ expect(errors['key']).to eq('is not a valid IPv6 address')
85
+ end
86
+
87
+ it 'rejects empty strings' do
88
+ errors = {}
89
+ validator.validate('key', '', {}, errors)
90
+ expect(errors['key']).to eq('is not a valid IPv6 address')
91
+ end
92
+
93
+ it 'rejects incomplete addresses' do
94
+ errors = {}
95
+ validator.validate('key', '2001:db8:', {}, errors)
96
+ expect(errors['key']).to eq('is not a valid IPv6 address')
97
+ end
98
+ end
99
+ end