respect 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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