respect 0.1.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 (97) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +289 -0
  3. data/RELATED_WORK.md +40 -0
  4. data/RELEASE_NOTES.md +23 -0
  5. data/Rakefile +31 -0
  6. data/STATUS_MATRIX.html +137 -0
  7. data/lib/respect.rb +231 -0
  8. data/lib/respect/any_schema.rb +22 -0
  9. data/lib/respect/array_def.rb +28 -0
  10. data/lib/respect/array_schema.rb +203 -0
  11. data/lib/respect/boolean_schema.rb +32 -0
  12. data/lib/respect/composite_schema.rb +86 -0
  13. data/lib/respect/core_statements.rb +206 -0
  14. data/lib/respect/datetime_schema.rb +27 -0
  15. data/lib/respect/def_without_name.rb +6 -0
  16. data/lib/respect/divisible_by_validator.rb +20 -0
  17. data/lib/respect/doc_helper.rb +24 -0
  18. data/lib/respect/doc_parser.rb +37 -0
  19. data/lib/respect/dsl_dumper.rb +181 -0
  20. data/lib/respect/equal_to_validator.rb +20 -0
  21. data/lib/respect/fake_name_proxy.rb +116 -0
  22. data/lib/respect/float_schema.rb +27 -0
  23. data/lib/respect/format_validator.rb +136 -0
  24. data/lib/respect/global_def.rb +79 -0
  25. data/lib/respect/greater_than_or_equal_to_validator.rb +19 -0
  26. data/lib/respect/greater_than_validator.rb +19 -0
  27. data/lib/respect/has_constraints.rb +34 -0
  28. data/lib/respect/hash_def.rb +40 -0
  29. data/lib/respect/hash_schema.rb +218 -0
  30. data/lib/respect/in_validator.rb +19 -0
  31. data/lib/respect/integer_schema.rb +27 -0
  32. data/lib/respect/ip_addr_schema.rb +23 -0
  33. data/lib/respect/ipv4_addr_schema.rb +27 -0
  34. data/lib/respect/ipv6_addr_schema.rb +27 -0
  35. data/lib/respect/items_def.rb +21 -0
  36. data/lib/respect/json_schema_html_formatter.rb +143 -0
  37. data/lib/respect/less_than_or_equal_to_validator.rb +19 -0
  38. data/lib/respect/less_than_validator.rb +19 -0
  39. data/lib/respect/match_validator.rb +19 -0
  40. data/lib/respect/max_length_validator.rb +20 -0
  41. data/lib/respect/min_length_validator.rb +20 -0
  42. data/lib/respect/multiple_of_validator.rb +10 -0
  43. data/lib/respect/null_schema.rb +26 -0
  44. data/lib/respect/numeric_schema.rb +33 -0
  45. data/lib/respect/org3_dumper.rb +213 -0
  46. data/lib/respect/regexp_schema.rb +19 -0
  47. data/lib/respect/schema.rb +285 -0
  48. data/lib/respect/schema_def.rb +16 -0
  49. data/lib/respect/string_schema.rb +21 -0
  50. data/lib/respect/unit_test_helper.rb +37 -0
  51. data/lib/respect/uri_schema.rb +23 -0
  52. data/lib/respect/utc_time_schema.rb +17 -0
  53. data/lib/respect/validator.rb +51 -0
  54. data/lib/respect/version.rb +3 -0
  55. data/test/any_schema_test.rb +79 -0
  56. data/test/array_def_test.rb +113 -0
  57. data/test/array_schema_test.rb +487 -0
  58. data/test/boolean_schema_test.rb +89 -0
  59. data/test/composite_schema_test.rb +30 -0
  60. data/test/datetime_schema_test.rb +83 -0
  61. data/test/doc_helper_test.rb +34 -0
  62. data/test/doc_parser_test.rb +109 -0
  63. data/test/dsl_dumper_test.rb +395 -0
  64. data/test/fake_name_proxy_test.rb +138 -0
  65. data/test/float_schema_test.rb +146 -0
  66. data/test/format_validator_test.rb +224 -0
  67. data/test/hash_def_test.rb +126 -0
  68. data/test/hash_schema_test.rb +613 -0
  69. data/test/integer_schema_test.rb +142 -0
  70. data/test/ip_addr_schema_test.rb +78 -0
  71. data/test/ipv4_addr_schema_test.rb +71 -0
  72. data/test/ipv6_addr_schema_test.rb +71 -0
  73. data/test/json_schema_html_formatter_test.rb +214 -0
  74. data/test/null_schema_test.rb +46 -0
  75. data/test/numeric_schema_test.rb +294 -0
  76. data/test/org3_dumper_test.rb +784 -0
  77. data/test/regexp_schema_test.rb +54 -0
  78. data/test/respect_test.rb +108 -0
  79. data/test/schema_def_test.rb +405 -0
  80. data/test/schema_test.rb +290 -0
  81. data/test/string_schema_test.rb +209 -0
  82. data/test/support/circle.rb +11 -0
  83. data/test/support/color.rb +24 -0
  84. data/test/support/point.rb +11 -0
  85. data/test/support/respect/circle_schema.rb +16 -0
  86. data/test/support/respect/color_def.rb +19 -0
  87. data/test/support/respect/color_schema.rb +33 -0
  88. data/test/support/respect/point_schema.rb +19 -0
  89. data/test/support/respect/rgba_schema.rb +20 -0
  90. data/test/support/respect/universal_validator.rb +25 -0
  91. data/test/support/respect/user_macros.rb +12 -0
  92. data/test/support/rgba.rb +11 -0
  93. data/test/test_helper.rb +90 -0
  94. data/test/uri_schema_test.rb +54 -0
  95. data/test/utc_time_schema_test.rb +63 -0
  96. data/test/validator_test.rb +22 -0
  97. metadata +288 -0
@@ -0,0 +1,126 @@
1
+ require "test_helper"
2
+
3
+ class HashDefTest < Test::Unit::TestCase
4
+
5
+ def test_hash_schema_definition_accept_integer
6
+ s = Respect::HashSchema.define do |s|
7
+ s.integer "test", equal_to: 15
8
+ s.extra do |s|
9
+ s.integer "opt", equal_to: 43
10
+ end
11
+ end
12
+ assert_schema_validate s, { "test" => "15", "opt" => "43" }
13
+ assert_schema_validate s, { "test" => 15, "opt" => 43 }
14
+ end
15
+
16
+ def test_hash_schema_definition_accept_null
17
+ s = Respect::HashSchema.define do |s|
18
+ s.null "test"
19
+ s.extra do |s|
20
+ s.null "opt"
21
+ end
22
+ end
23
+ assert_schema_validate s, { "test" => "null", "opt" => "null" }
24
+ assert_schema_validate s, { "test" => nil, "opt" => nil }
25
+ end
26
+
27
+ def test_hash_schema_definition_accept_float
28
+ s = Respect::HashSchema.define do |s|
29
+ s.float "test", equal_to: 1.5
30
+ s.extra do |s|
31
+ s.float "opt", equal_to: 4.3
32
+ end
33
+ end
34
+ assert_schema_validate s, { "test" => "1.5", "opt" => "4.3" }
35
+ assert_schema_validate s, { "test" => 1.5, "opt" => 4.3 }
36
+ end
37
+
38
+ def test_hash_schema_definition_accept_boolean
39
+ s = Respect::HashSchema.define do |s|
40
+ s.boolean "test", equal_to: false
41
+ s.extra do |s|
42
+ s.boolean "opt", equal_to: false
43
+ end
44
+ end
45
+ assert_schema_validate s, { "test" => "false", "opt" => "false" }
46
+ assert_schema_validate s, { "test" => false, "opt" => false }
47
+ end
48
+
49
+ def test_hash_schema_definition_accept_array
50
+ s = Respect::HashSchema.define do |s|
51
+ s.array "test"
52
+ s.extra do |s|
53
+ s.array "opt"
54
+ end
55
+ end
56
+ assert_schema_validate s, { "test" => [], "opt" => [] }
57
+ end
58
+
59
+ def test_cannot_overwrite_property
60
+ assert_raise(Respect::InvalidSchemaError) do
61
+ Respect::HashSchema.define do |s|
62
+ s.integer "id", equal_to: 42
63
+ s.integer "id", equal_to: 51
64
+ end
65
+ end
66
+ end
67
+
68
+ def test_extra_properties_overwrite_expected_ones
69
+ assert_raise(Respect::InvalidSchemaError) do
70
+ Respect::HashSchema.define(strict: true) do |s|
71
+ s.integer "test", equal_to: 42
72
+ s.extra do |s|
73
+ s.integer "test", equal_to: 51
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ def test_hash_scope_accept_options
80
+ s = Respect::HashSchema.define do |s|
81
+ s.hash "test", strict: true do |s|
82
+ s.integer "test", equal_to: 42
83
+ end
84
+ end
85
+ assert s["test"].options[:strict]
86
+ end
87
+
88
+ def test_factor_options_with_with_options
89
+ s = Respect::HashSchema.define do |s|
90
+ s.integer "test", equal_to: 42
91
+ s.with_options required: false do |s|
92
+ assert_nothing_raised("fake name proxy") do
93
+ s.target
94
+ end
95
+ s.doc "doc opt1"
96
+ s.integer "opt1", greater_than: 0
97
+ s.doc "doc opt2"
98
+ s.integer "opt2", less_than: 0
99
+ end
100
+ end
101
+ assert_equal(42, s["test"].options[:equal_to])
102
+ assert_equal(true, s["test"].options[:required])
103
+ assert_equal(false, s["opt1"].options[:required])
104
+ assert_equal(0, s["opt1"].options[:greater_than])
105
+ assert_equal("doc opt1", s["opt1"].doc)
106
+ assert_equal(false, s["opt2"].options[:required])
107
+ assert_equal(0, s["opt2"].options[:less_than])
108
+ assert_equal("doc opt2", s["opt2"].doc)
109
+ end
110
+
111
+ def test_key_assignment_with_string_value_create_string_schema_with_equal_to_validator
112
+ h = Respect::HashSchema.define do |h|
113
+ h["key"] = "value"
114
+ end
115
+ assert_equal Respect::StringSchema.new(equal_to: "value"), h["key"]
116
+ end
117
+
118
+ def test_assign_an_object_create_any_with_equal_to_validating_its_string_representation
119
+ o = Object.new
120
+ h = Respect::HashSchema.define do |h|
121
+ h["any"] = o
122
+ end
123
+ s = Respect::AnySchema.new(equal_to: o.to_s)
124
+ assert_equal s, h["any"]
125
+ end
126
+ end
@@ -0,0 +1,613 @@
1
+ require "test_helper"
2
+
3
+ class HashSchemaTest < Test::Unit::TestCase
4
+ def test_validate_return_true_on_success
5
+ s = Respect::HashSchema.define do |s|
6
+ s.integer "id", equal_to: 42
7
+ end
8
+ assert s.validate({ "id" => 42 })
9
+
10
+ s = Respect::HashSchema.new
11
+ assert s.validate({})
12
+ end
13
+
14
+ def test_validate_raise_exception_on_error
15
+ s = Respect::HashSchema.define do |s|
16
+ s.integer "id", equal_to: 42
17
+ end
18
+ assert_raise(Respect::ValidationError) do
19
+ s.validate({ "asdf" => 42 })
20
+ end
21
+ end
22
+
23
+ def test_validate?
24
+ s = Respect::HashSchema.define do |s|
25
+ s.integer "id", equal_to: 42
26
+ end
27
+ assert_schema_invalidate s, { "asdf" => 42 }
28
+ end
29
+
30
+ def test_recursive_schema
31
+ s = Respect::HashSchema.define do |s|
32
+ s.hash "test" do |s|
33
+ s.integer "test", equal_to: 42
34
+ end
35
+ end
36
+ assert_schema_validate s, { "test" => { "test" => 42 } }
37
+ end
38
+
39
+ def test_hash_validate
40
+ s = Respect::HashSchema.define do |s|
41
+ s.integer "test", equal_to: 42
42
+ end
43
+ [
44
+ [ {}, false, "empty hash" ],
45
+ [ { "foo" => 42 }, false, "no expected property" ],
46
+ [ { "test" => 42, "foo" => 60 }, true, "extra property" ],
47
+ ].each do |data|
48
+ assert_schema_validation_is data[1], s, data[0], data[2]
49
+ end
50
+ end
51
+
52
+ def test_empty_hash_schema_validate_all_hash
53
+ s = Respect::HashSchema.define do |s|
54
+ end
55
+ [
56
+ [ {}, true, "empty hash" ],
57
+ [ { "foo" => 42 }, true, "no expected property" ],
58
+ [ { "test" => 42, "foo" => 60 }, true, "extra property" ],
59
+ ].each do |data|
60
+ assert_schema_validation_is data[1], s, data[0], data[2]
61
+ end
62
+ end
63
+
64
+ def test_hash_schema_do_not_validate_other_type
65
+ s = Respect::HashSchema.define do |s|
66
+ s.integer "test", greater_than: 0
67
+ end
68
+ [
69
+ [ { "test" => 1 } ],
70
+ 42,
71
+ "test",
72
+ nil,
73
+ ].each do |doc|
74
+ assert_raise(Respect::ValidationError) do
75
+ s.validate(doc)
76
+ end
77
+ end
78
+ end
79
+
80
+ def test_hash_stricly_validate
81
+ s = Respect::Schema.define do |s|
82
+ s.hash strict: true do |s|
83
+ s.integer "p1", equal_to: 3
84
+ s.string "p2", equal_to: "val"
85
+ end
86
+ end
87
+ [
88
+ [ { "p1" => 3, "p2" => "val" }, true, "valid", ],
89
+ [ { "p1" => 3 }, false, "not enough properties" ],
90
+ [ { "p1" => 3, "p2" => "val", "additional" => "foo" }, false, "too many properties" ],
91
+ ].each do |data|
92
+ assert_schema_validation_is data[1], s, data[0], data[2]
93
+ end
94
+ end
95
+
96
+ def test_optional_property
97
+ s1 = Respect::HashSchema.define do |s|
98
+ s.integer "test", equal_to: 42
99
+ s.extra do |s|
100
+ s.integer "opt", equal_to: 51
101
+ end
102
+ end
103
+ s2 = Respect::HashSchema.define do |s|
104
+ s.integer "test", equal_to: 42
105
+ s.integer "opt", equal_to: 51, required: false
106
+ end
107
+ [
108
+ [ {}, false, "empty hash", ],
109
+ [ { "test" => 42 }, true, "only expected property", ],
110
+ [ { "test" => 42, "opt" => 51 }, true, "expected and optional properties" ],
111
+ [ { "test" => 42, "opt" => 64 }, false, "invalid optional property" ],
112
+ [ { "test" => 54, "opt" => 51 }, false, "invalid expected property" ],
113
+ [ { "opt" => 51 }, false, "only optional property" ],
114
+ [ { "test" => 42, "extra" => 1, "opt" => 51 }, true, "extra and optional property" ],
115
+ [ { "test" => 42, "extra" => 1 }, true, "extra property" ],
116
+ ].each do |data|
117
+ assert_schema_validation_is data[1], s1, data[0], data[2]
118
+ assert_schema_validation_is data[1], s2, data[0], data[2]
119
+ end
120
+ end
121
+
122
+ def test_optional_property_with_strict_validation
123
+ s = Respect::HashSchema.define(strict: true) do |s|
124
+ s.integer "test", equal_to: 42
125
+ s.extra do |s|
126
+ s.integer "opt", equal_to: 51
127
+ end
128
+ end
129
+ [
130
+ [ { "test" => 42, "extra" => 1, "opt" => 51 }, false, "extra and optional property" ],
131
+ [ { "test" => 42, "extra" => 1 }, false, "extra property" ],
132
+ [ { "test" => 42, "opt" => 51 }, true, "required and optional properties" ],
133
+ [ { "test" => 42, }, true, "only required property" ],
134
+ ].each do |data|
135
+ assert_schema_validation_is data[1], s, data[0], data[2]
136
+ end
137
+ end
138
+
139
+ def test_symbolized_property_name_are_kept_verbatim
140
+ s = Respect::HashSchema.define do |s|
141
+ s.integer :test, equal_to: 42
142
+ s.extra do |s|
143
+ s.integer :opt, equal_to: 51
144
+ end
145
+ s.string :foo, required: false
146
+ s.hash :bar, default: { a: "b" }
147
+ s.string "a_string"
148
+ s.string /foo/
149
+ end
150
+ assert s.properties.has_key?(:test)
151
+ assert s.properties.has_key?("a_string")
152
+ assert s.properties.has_key?(/foo/)
153
+ assert s.optional_properties.has_key?(:opt)
154
+ assert s.optional_properties.has_key?(:foo)
155
+ assert s.optional_properties.has_key?(:bar)
156
+ end
157
+
158
+ def test_symbol_key_are_stringified_at_validation
159
+ s = Respect::HashSchema.define do |s|
160
+ s.integer :test, equal_to: 42
161
+ end
162
+ assert_schema_validate s, { "test" => 42 }
163
+ assert s.properties.has_key?(:test)
164
+ end
165
+
166
+ def test_can_define_empty_strict_hash_schema
167
+ s = Respect::Schema.define do |s|
168
+ s.hash strict: true
169
+ end
170
+ assert_equal true, s.options[:strict]
171
+ assert_schema_validate s, {}
172
+ assert_schema_invalidate s, { a: "b" }
173
+ end
174
+
175
+ def test_can_define_empty_hash_schema
176
+ s = Respect::Schema.define do |s|
177
+ s.hash
178
+ end
179
+ assert_equal false, s.options[:strict]
180
+ assert_schema_validate s, {}
181
+ assert_schema_validate s, { a: "b" }
182
+ end
183
+
184
+ def test_access_to_extra_properties
185
+ s = Respect::HashSchema.define do |s|
186
+ s.integer :test, equal_to: 42
187
+ s.extra do |s|
188
+ s.integer :opt, equal_to: 51
189
+ end
190
+ s.string :foo, required: false
191
+ s.hash :bar, default: { a: "b" }
192
+ end
193
+ assert s.properties.has_key?(:test)
194
+ assert s.properties.has_key?(:opt)
195
+ assert s.properties.has_key?(:foo)
196
+ assert s.properties.has_key?(:bar)
197
+ assert !s.optional_properties.has_key?(:test)
198
+ assert s.optional_properties.has_key?(:opt)
199
+ assert s.optional_properties.has_key?(:foo)
200
+ assert s.optional_properties.has_key?(:bar)
201
+ end
202
+
203
+ def test_default_value_used_when_property_unset
204
+ s = Respect::HashSchema.define do |s|
205
+ s.integer "test", default: 42
206
+ end
207
+ # Empty hash.
208
+ s.validate({})
209
+ assert_equal({ "test" => 42 }, s.sanitized_object)
210
+ # Hash with extra property.
211
+ s.validate({ "foo" => 51 })
212
+ assert_equal({ "test" => 42 }, s.sanitized_object)
213
+ end
214
+
215
+ def test_default_value_do_not_overwrite_defined_one
216
+ s = Respect::HashSchema.define do |s|
217
+ s.integer "test", default: 42
218
+ end
219
+ s.validate({ "test" => 54 })
220
+ assert_equal({ "test" => 54 }, s.sanitized_object)
221
+ end
222
+
223
+ def test_validate_matched_properties
224
+ s = Respect::HashSchema.define do |s|
225
+ s.integer /test/, equal_to: 42
226
+ end
227
+ assert_schema_validate s, { "test" => 42, "_test_" => 42, "unmatch" => 51 }
228
+ assert_schema_invalidate s, { "test" => 42, "_test_" => 51, "unmatch" => 51 }
229
+ end
230
+
231
+ def test_integer_invalid_property_name
232
+ assert_raises(Respect::InvalidSchemaError) do
233
+ Respect::HashSchema.define do |s|
234
+ s.integer 42, equal_to: 42
235
+ end
236
+ end
237
+ end
238
+
239
+ def test_access_to_pattern_properties
240
+ s = Respect::HashSchema.define do |s|
241
+ s.integer "test", equal_to: 42
242
+ s.string /foo/, equal_to: 51
243
+ end
244
+ assert s.properties.has_key?("test")
245
+ assert s.properties.has_key?(/foo/)
246
+ assert s.pattern_properties.has_key?(/foo/)
247
+ assert !s.pattern_properties.has_key?("test")
248
+ end
249
+
250
+ def test_hash_schema_statement_expect_name_argument
251
+ [
252
+ :hash,
253
+ :integer,
254
+ :string,
255
+ :array,
256
+ :any,
257
+ :boolean,
258
+ :null,
259
+ :float,
260
+ :numeric,
261
+ ].each do |meth_name|
262
+ assert_raise(Respect::InvalidSchemaError) do
263
+ Respect::HashSchema.define do |s|
264
+ s.__send__(meth_name, {})
265
+ end
266
+ end
267
+ assert_raise(Respect::InvalidSchemaError) do
268
+ Respect::HashSchema.define do |s|
269
+ s.extra do |s|
270
+ s.__send__(meth_name, {})
271
+ end
272
+ end
273
+ end
274
+ end
275
+ end
276
+
277
+ def test_doc_updated_with_sanitized_value
278
+ s = Respect::HashSchema.define do |s|
279
+ s.integer "int", equal_to: 42
280
+ end
281
+ doc = { "int" => "42" }
282
+ assert_not_nil s.validate!(doc)
283
+ assert_equal 42, doc["int"]
284
+ end
285
+
286
+ def test_doc_updated_with_custom_type_sanitized_value
287
+ s = Respect::HashSchema.define do |s|
288
+ s.circle "circle"
289
+ end
290
+ doc = { "circle" => { "center" => { "x" => "1.0", "y" => "2.0" }, "radius" => "5.5" } }
291
+ assert s.validate!(doc)
292
+ assert_equal Circle.new(Point.new(1.0, 2.0), 5.0), doc["circle"]
293
+ end
294
+
295
+ def test_recursive_doc_updated_with_sanitized_value
296
+ s = Respect::HashSchema.define do |s|
297
+ s.integer "int", equal_to: 42
298
+ s.hash "obj" do |s|
299
+ s.integer "int", equal_to: 51
300
+ end
301
+ end
302
+ doc = { "int" => "42", "obj" => { "int" => "51" } }
303
+ assert_not_nil s.validate!(doc)
304
+ assert_equal({ "int" => 42, "obj" => { "int" => 51 } }, doc)
305
+ end
306
+
307
+ def test_only_update_validated_properties
308
+ s = Respect::HashSchema.define do |s|
309
+ s.integer "int", equal_to: 42
310
+ end
311
+ doc = { "int" => "42", "not_validated" => "51" }
312
+ assert_not_nil s.validate!(doc)
313
+ assert_equal({ "int" => 42, "not_validated" => "51" }, doc)
314
+ end
315
+
316
+ def test_only_update_recursive_validated_properties
317
+ s = Respect::HashSchema.define do |s|
318
+ s.hash "obj" do |s|
319
+ s.integer "int", equal_to: 42
320
+ end
321
+ end
322
+ doc = { "obj" => { "int" => "42", "not_validated" => "51" } }
323
+ assert_not_nil s.validate!(doc)
324
+ assert_equal({ "obj" => { "int" => 42, "not_validated" => "51" } }, doc)
325
+ end
326
+
327
+ def test_sanitize_simple_document
328
+ s = Respect::HashSchema.define do |s|
329
+ s.integer "id", equal_to: 42
330
+ end
331
+ doc = { "id" => "42" }
332
+ assert_nil s.sanitized_object
333
+ s.validate(doc)
334
+ assert_equal({ "id" => "42" }, doc)
335
+ assert_equal({ "id" => 42 }, s.sanitized_object)
336
+ end
337
+
338
+ def test_sanitize_recursive_document
339
+ s = Respect::HashSchema.define do |s|
340
+ s.integer "id", equal_to: 42
341
+ s.hash "obj" do |s|
342
+ s.integer "id", equal_to: 51
343
+ end
344
+ end
345
+ doc = { "id" => "42", "obj" => { "id" => "51" } }
346
+ assert_nil s.sanitized_object
347
+ s.validate(doc)
348
+ assert_equal({ "id" => "42", "obj" => { "id" => "51" } }, doc)
349
+ assert_equal({ "id" => 42, "obj" => { "id" => 51 } }, s.sanitized_object)
350
+ end
351
+
352
+ def test_do_not_sanitize_unvalidated_optional_property
353
+ s = Respect::HashSchema.define do |s|
354
+ s.integer "id1", equal_to: 42
355
+ s.integer "id2", equal_to: 51, required: false
356
+ end
357
+ doc = { "id1" => "42" }
358
+ assert_nil s.sanitized_object
359
+ s.validate(doc)
360
+ assert_equal({ "id1" => "42" }, doc)
361
+ assert_equal({ "id1" => 42 }, s.sanitized_object)
362
+ end
363
+
364
+ def test_hash_schema_merge_default_options
365
+ s = Respect::HashSchema.new
366
+ assert_equal true, s.options[:required]
367
+ assert_equal false, s.options[:strict]
368
+ end
369
+
370
+ def test_hash_schema_merge_options
371
+ s = Respect::HashSchema.new(opt: 1, strict: true)
372
+ assert_equal true, s.options[:required]
373
+ assert_equal true, s.options[:strict]
374
+ assert_equal 1, s.options[:opt]
375
+ end
376
+
377
+ def test_non_default_options
378
+ s = Respect::HashSchema.new(opt: 1, strict: true)
379
+ opts = s.non_default_options
380
+ assert !opts.has_key?(:required)
381
+ assert_equal true, opts[:strict]
382
+ assert_equal 1, opts[:opt]
383
+ end
384
+
385
+ def test_has_property
386
+ s1 = Respect::HashSchema.define do |s|
387
+ s.integer "s11"
388
+ end
389
+ assert s1.has_property?("s11")
390
+ assert !s1.has_property?("not a property")
391
+ end
392
+
393
+ def test_merge_hash_schema_in_place
394
+ s1 = Respect::HashSchema.define opt0: false, opt1: false do |s|
395
+ s.integer "s11"
396
+ s.integer "s12"
397
+ end
398
+ s2 = Respect::HashSchema.define opt1: true, opt2: true do |s|
399
+ s.integer "s21"
400
+ s.integer "s22"
401
+ end
402
+ s1.merge!(s2)
403
+ %w(s11 s12 s21 s22).each do |prop|
404
+ assert s1.has_property?(prop), "has prop #{prop}"
405
+ end
406
+ assert_equal false, s1.options[:opt0]
407
+ assert_equal true, s1.options[:opt1]
408
+ assert_equal true, s1.options[:opt2]
409
+ end
410
+
411
+ def test_merge_hash_schema
412
+ s1 = Respect::HashSchema.define do |s|
413
+ s.integer "s11"
414
+ s.integer "s12"
415
+ end
416
+ s2 = Respect::HashSchema.define do |s|
417
+ s.integer "s21"
418
+ s.integer "s22"
419
+ end
420
+ s3 = s1.merge(s2)
421
+ assert(s3.object_id != s1.object_id)
422
+ assert_equal 2, s1.properties.size
423
+ assert_equal 2, s2.properties.size
424
+ assert_equal 4, s3.properties.size
425
+ %w(s11 s12 s21 s22).each do |prop|
426
+ assert s3.has_property?(prop), "has prop #{prop}"
427
+ end
428
+ end
429
+
430
+ def test_merge_uses_merge_shebang_on_duplicata
431
+ s1 = Respect::HashSchema.new
432
+ s2 = Respect::HashSchema.new
433
+ result = Respect::HashSchema.new
434
+ dup_s1 = Respect::HashSchema.new
435
+ s1.stubs(:dup).returns(dup_s1).once
436
+ dup_s1.stubs(:merge!).with(s2).returns(result).once
437
+ assert_equal result, s1.merge(s2)
438
+ end
439
+
440
+ def test_dup_duplicate_properties_and_options
441
+ s1 = Respect::HashSchema.define strict: true do |s|
442
+ s.integer "s11"
443
+ end
444
+ s2 = s1.dup
445
+ assert(s2.object_id != s1.object_id)
446
+ assert(s1.properties.object_id != s2.properties.object_id)
447
+ assert(s2.has_property?("s11"))
448
+ assert_equal(true, s2.options[:strict])
449
+ assert(s1["s11"].object_id == s2["s11"].object_id)
450
+ end
451
+
452
+ def test_eval_add_more_properties
453
+ s1 = Respect::HashSchema.define do |s|
454
+ s.integer "s11"
455
+ end
456
+ assert s1.has_property?("s11")
457
+ assert !s1.has_property?("new_prop")
458
+ s1.eval do |s|
459
+ s.integer "new_prop"
460
+ end
461
+ assert s1.has_property?("s11")
462
+ assert s1.has_property?("new_prop")
463
+ end
464
+
465
+ def test_documented_properties
466
+ s = Respect::HashSchema.define do |s|
467
+ s.integer "documented"
468
+ s.integer "documented_with_text", doc: "a title"
469
+ s.integer "nodoc", doc: false
470
+ end
471
+ e = %w(documented documented_with_text).sort
472
+ assert_equal e, s.documented_properties.keys.sort
473
+ end
474
+
475
+ def test_duplicata_are_equal
476
+ s1 = Respect::HashSchema.define do |s|
477
+ s.integer "i"
478
+ end
479
+ assert_equal s1, s1.dup
480
+ end
481
+
482
+ def test_schema_differs_from_options
483
+ s1 = Respect::HashSchema.new(required: true)
484
+ s2 = Respect::HashSchema.new(required: false)
485
+ assert(s1 != s2)
486
+ end
487
+
488
+ def test_schema_differs_from_doc
489
+ s1 = Respect::Schema.define do |s|
490
+ s.doc "hey"
491
+ s.hash
492
+ end
493
+ s2 = Respect::Schema.define do |s|
494
+ s.doc "ho"
495
+ s.hash
496
+ end
497
+ assert(s1 != s2)
498
+ end
499
+
500
+ def test_schema_differs_from_properties
501
+ s1 = Respect::HashSchema.new
502
+ s1["i1"] = Respect::IntegerSchema.new
503
+ s2 = Respect::HashSchema.new
504
+ s2["i2"] = Respect::IntegerSchema.new
505
+ assert(s1 != s2)
506
+ end
507
+
508
+ def test_indifferent_access_to_doc_key
509
+ s = Respect::HashSchema.define do |s|
510
+ s.integer "i", equal_to: 42
511
+ end
512
+ assert_schema_validate(s, { i: "42" })
513
+ assert_schema_validate(s, { "i" => "42" })
514
+ assert_schema_invalidate(s, { })
515
+ end
516
+
517
+ def test_doc_sanitized_in_place_keep_original_access
518
+ s = Respect::HashSchema.define do |s|
519
+ s.integer "i_string", equal_to: 42
520
+ s.integer :i_symbol, equal_to: 51
521
+ end
522
+
523
+ doc = { i_string: "42", "i_symbol" => "51" }
524
+ assert_schema_validate(s, doc)
525
+ assert !doc.is_a?(HashWithIndifferentAccess)
526
+ assert_equal(42, s.sanitized_object[:i_string])
527
+ assert_equal(42, s.sanitized_object["i_string"])
528
+ assert_equal(51, s.sanitized_object[:i_symbol])
529
+ assert_equal(51, s.sanitized_object["i_symbol"])
530
+
531
+ assert s.sanitized_object.is_a?(HashWithIndifferentAccess)
532
+ assert_equal({ "i_string" => 42, "i_symbol" => 51 }, s.sanitized_object)
533
+
534
+ s.sanitize_object!(doc)
535
+ assert_equal({ i_string: 42, "i_symbol" => 51 }, doc)
536
+ end
537
+
538
+ def test_indifferent_access_to_doc_nil_values
539
+ s = Respect::HashSchema.define do |s|
540
+ s.null "n"
541
+ end
542
+ assert_schema_validate(s, { n: nil })
543
+ assert_schema_validate(s, { "n" => nil })
544
+ assert_schema_invalidate(s, { })
545
+ end
546
+
547
+ def test_indifferent_access_to_doc_false_values
548
+ s = Respect::HashSchema.define do |s|
549
+ s.boolean "b", equal_to: false
550
+ end
551
+ assert_schema_validate(s, { b: false })
552
+ assert_schema_validate(s, { "b" => false })
553
+ assert_schema_invalidate(s, { })
554
+ end
555
+
556
+ def test_sanitized_object_only_include_validated_keys
557
+ s = Respect::HashSchema.define do |s|
558
+ s.integer "i"
559
+ end
560
+ assert_schema_validate(s, { i: "42", foo: "bar" })
561
+ assert_equal({ "i" => 42 }, s.sanitized_object)
562
+ end
563
+
564
+ def test_sanitized_object_include_optional_keys_when_present
565
+ s = Respect::HashSchema.define do |s|
566
+ s.integer "i", required: false
567
+ end
568
+ assert_schema_validate(s, { i: "42", foo: "bar" })
569
+ assert_equal({ "i" => 42 }, s.sanitized_object)
570
+ assert_schema_validate(s, { foo: "bar" })
571
+ assert_equal({ }, s.sanitized_object)
572
+ end
573
+
574
+ def test_failed_validation_reset_sanitized_object
575
+ s = Respect::HashSchema.define do |s|
576
+ s.integer "i", equal_to: 42
577
+ end
578
+ assert_schema_validate(s, {i: 42})
579
+ assert_equal({"i" => 42}, s.sanitized_object)
580
+ assert_schema_invalidate(s, {i: 51})
581
+ assert_equal(nil, s.sanitized_object)
582
+ end
583
+
584
+ def test_integer_property_accept_nil
585
+ s = Respect::HashSchema.define do |s|
586
+ s.integer "i", allow_nil: true
587
+ end
588
+ assert_schema_validate(s, { i: nil })
589
+ assert_equal({ "i" => nil }, s.sanitized_object)
590
+ assert_schema_validate(s, { i: 42 })
591
+ assert_equal({ "i" => 42 }, s.sanitized_object)
592
+ assert_schema_invalidate(s, { i: "wrong" })
593
+ assert_equal(nil, s.sanitized_object)
594
+ end
595
+
596
+ def test_allow_nil
597
+ s = Respect::HashSchema.new(allow_nil: true)
598
+ assert_schema_validate s, nil
599
+ assert_equal(nil, s.sanitized_object)
600
+ assert_schema_validate s, {}
601
+ assert_equal({}, s.sanitized_object)
602
+ end
603
+
604
+ def test_disallow_nil
605
+ s = Respect::HashSchema.new
606
+ assert !s.allow_nil?
607
+ exception = assert_exception(Respect::ValidationError) { s.validate(nil) }
608
+ assert_match exception.message, /\bHashSchema\b/
609
+ assert_equal(nil, s.sanitized_object)
610
+ assert_schema_validate s, {}
611
+ assert_equal({}, s.sanitized_object)
612
+ end
613
+ end