schemacop 2.4.6 → 3.0.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (174) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.releaser_config +0 -1
  4. data/.rubocop.yml +25 -1
  5. data/.travis.yml +3 -1
  6. data/CHANGELOG.md +33 -0
  7. data/README.md +53 -710
  8. data/README_V2.md +775 -0
  9. data/README_V3.md +1197 -0
  10. data/Rakefile +8 -12
  11. data/VERSION +1 -1
  12. data/lib/schemacop.rb +35 -36
  13. data/lib/schemacop/base_schema.rb +37 -0
  14. data/lib/schemacop/railtie.rb +10 -0
  15. data/lib/schemacop/schema.rb +1 -60
  16. data/lib/schemacop/schema2.rb +22 -0
  17. data/lib/schemacop/schema3.rb +21 -0
  18. data/lib/schemacop/scoped_env.rb +25 -13
  19. data/lib/schemacop/v2.rb +25 -0
  20. data/lib/schemacop/{caster.rb → v2/caster.rb} +16 -2
  21. data/lib/schemacop/{collector.rb → v2/collector.rb} +5 -2
  22. data/lib/schemacop/{dupper.rb → v2/dupper.rb} +1 -1
  23. data/lib/schemacop/{field_node.rb → v2/field_node.rb} +4 -3
  24. data/lib/schemacop/v2/node.rb +142 -0
  25. data/lib/schemacop/{node_resolver.rb → v2/node_resolver.rb} +1 -1
  26. data/lib/schemacop/v2/node_supporting_field.rb +70 -0
  27. data/lib/schemacop/{node_supporting_type.rb → v2/node_supporting_type.rb} +8 -5
  28. data/lib/schemacop/{node_with_block.rb → v2/node_with_block.rb} +3 -2
  29. data/lib/schemacop/v2/root_node.rb +0 -0
  30. data/lib/schemacop/v2/validator/array_validator.rb +32 -0
  31. data/lib/schemacop/{validator → v2/validator}/boolean_validator.rb +1 -1
  32. data/lib/schemacop/v2/validator/float_validator.rb +7 -0
  33. data/lib/schemacop/v2/validator/hash_validator.rb +37 -0
  34. data/lib/schemacop/v2/validator/integer_validator.rb +7 -0
  35. data/lib/schemacop/{validator → v2/validator}/nil_validator.rb +1 -1
  36. data/lib/schemacop/v2/validator/number_validator.rb +21 -0
  37. data/lib/schemacop/v2/validator/object_validator.rb +29 -0
  38. data/lib/schemacop/v2/validator/string_validator.rb +39 -0
  39. data/lib/schemacop/{validator → v2/validator}/symbol_validator.rb +1 -1
  40. data/lib/schemacop/v3.rb +45 -0
  41. data/lib/schemacop/v3/all_of_node.rb +27 -0
  42. data/lib/schemacop/v3/any_of_node.rb +28 -0
  43. data/lib/schemacop/v3/array_node.rb +218 -0
  44. data/lib/schemacop/v3/boolean_node.rb +16 -0
  45. data/lib/schemacop/v3/combination_node.rb +45 -0
  46. data/lib/schemacop/v3/context.rb +17 -0
  47. data/lib/schemacop/v3/dsl_scope.rb +46 -0
  48. data/lib/schemacop/v3/global_context.rb +114 -0
  49. data/lib/schemacop/v3/hash_node.rb +258 -0
  50. data/lib/schemacop/v3/integer_node.rb +13 -0
  51. data/lib/schemacop/v3/is_not_node.rb +32 -0
  52. data/lib/schemacop/v3/node.rb +219 -0
  53. data/lib/schemacop/v3/node_registry.rb +49 -0
  54. data/lib/schemacop/v3/number_node.rb +18 -0
  55. data/lib/schemacop/v3/numeric_node.rb +76 -0
  56. data/lib/schemacop/v3/object_node.rb +40 -0
  57. data/lib/schemacop/v3/one_of_node.rb +28 -0
  58. data/lib/schemacop/v3/reference_node.rb +49 -0
  59. data/lib/schemacop/v3/result.rb +58 -0
  60. data/lib/schemacop/v3/string_node.rb +124 -0
  61. data/lib/schemacop/v3/symbol_node.rb +13 -0
  62. data/schemacop.gemspec +24 -27
  63. data/test/lib/test_helper.rb +152 -0
  64. data/test/schemas/nested/group.rb +6 -0
  65. data/test/schemas/user.rb +7 -0
  66. data/test/unit/schemacop/v2/casting_test.rb +120 -0
  67. data/test/unit/schemacop/v2/collector_test.rb +47 -0
  68. data/test/unit/schemacop/v2/custom_check_test.rb +95 -0
  69. data/test/unit/schemacop/v2/custom_if_test.rb +97 -0
  70. data/test/unit/schemacop/v2/defaults_test.rb +95 -0
  71. data/test/unit/schemacop/v2/empty_test.rb +16 -0
  72. data/test/unit/schemacop/v2/nil_dis_allow_test.rb +43 -0
  73. data/test/unit/schemacop/v2/node_resolver_test.rb +28 -0
  74. data/test/unit/schemacop/v2/short_forms_test.rb +351 -0
  75. data/test/unit/schemacop/v2/types_test.rb +88 -0
  76. data/test/unit/schemacop/v2/validator_array_test.rb +99 -0
  77. data/test/unit/schemacop/v2/validator_boolean_test.rb +17 -0
  78. data/test/unit/schemacop/v2/validator_float_test.rb +59 -0
  79. data/test/unit/schemacop/v2/validator_hash_test.rb +95 -0
  80. data/test/unit/schemacop/v2/validator_integer_test.rb +48 -0
  81. data/test/unit/schemacop/v2/validator_nil_test.rb +15 -0
  82. data/test/unit/schemacop/v2/validator_number_test.rb +62 -0
  83. data/test/unit/schemacop/v2/validator_object_test.rb +141 -0
  84. data/test/unit/schemacop/v2/validator_string_test.rb +78 -0
  85. data/test/unit/schemacop/v2/validator_symbol_test.rb +18 -0
  86. data/test/unit/schemacop/v3/all_of_node_test.rb +198 -0
  87. data/test/unit/schemacop/v3/any_of_node_test.rb +218 -0
  88. data/test/unit/schemacop/v3/array_node_test.rb +815 -0
  89. data/test/unit/schemacop/v3/boolean_node_test.rb +126 -0
  90. data/test/unit/schemacop/v3/global_context_test.rb +164 -0
  91. data/test/unit/schemacop/v3/hash_node_test.rb +884 -0
  92. data/test/unit/schemacop/v3/integer_node_test.rb +323 -0
  93. data/test/unit/schemacop/v3/is_not_node_test.rb +173 -0
  94. data/test/unit/schemacop/v3/node_test.rb +148 -0
  95. data/test/unit/schemacop/v3/number_node_test.rb +292 -0
  96. data/test/unit/schemacop/v3/object_node_test.rb +170 -0
  97. data/test/unit/schemacop/v3/one_of_node_test.rb +187 -0
  98. data/test/unit/schemacop/v3/reference_node_test.rb +351 -0
  99. data/test/unit/schemacop/v3/string_node_test.rb +334 -0
  100. data/test/unit/schemacop/v3/symbol_node_test.rb +75 -0
  101. metadata +157 -150
  102. data/doc/Schemacop.html +0 -146
  103. data/doc/Schemacop/ArrayValidator.html +0 -329
  104. data/doc/Schemacop/BooleanValidator.html +0 -145
  105. data/doc/Schemacop/Caster.html +0 -379
  106. data/doc/Schemacop/Collector.html +0 -787
  107. data/doc/Schemacop/Dupper.html +0 -214
  108. data/doc/Schemacop/Exceptions.html +0 -115
  109. data/doc/Schemacop/Exceptions/InvalidSchemaError.html +0 -124
  110. data/doc/Schemacop/Exceptions/ValidationError.html +0 -124
  111. data/doc/Schemacop/FieldNode.html +0 -421
  112. data/doc/Schemacop/FloatValidator.html +0 -158
  113. data/doc/Schemacop/HashValidator.html +0 -293
  114. data/doc/Schemacop/IntegerValidator.html +0 -158
  115. data/doc/Schemacop/NilValidator.html +0 -145
  116. data/doc/Schemacop/Node.html +0 -1438
  117. data/doc/Schemacop/NodeResolver.html +0 -258
  118. data/doc/Schemacop/NodeSupportingField.html +0 -590
  119. data/doc/Schemacop/NodeSupportingType.html +0 -612
  120. data/doc/Schemacop/NodeWithBlock.html +0 -289
  121. data/doc/Schemacop/NumberValidator.html +0 -232
  122. data/doc/Schemacop/ObjectValidator.html +0 -298
  123. data/doc/Schemacop/RootNode.html +0 -171
  124. data/doc/Schemacop/Schema.html +0 -699
  125. data/doc/Schemacop/StringValidator.html +0 -295
  126. data/doc/Schemacop/SymbolValidator.html +0 -145
  127. data/doc/ScopedEnv.html +0 -351
  128. data/doc/_index.html +0 -379
  129. data/doc/class_list.html +0 -51
  130. data/doc/css/common.css +0 -1
  131. data/doc/css/full_list.css +0 -58
  132. data/doc/css/style.css +0 -496
  133. data/doc/file.README.html +0 -833
  134. data/doc/file_list.html +0 -56
  135. data/doc/frames.html +0 -17
  136. data/doc/index.html +0 -833
  137. data/doc/inheritance.graphml +0 -524
  138. data/doc/inheritance.pdf +0 -825
  139. data/doc/js/app.js +0 -303
  140. data/doc/js/full_list.js +0 -216
  141. data/doc/js/jquery.js +0 -4
  142. data/doc/method_list.html +0 -587
  143. data/doc/top-level-namespace.html +0 -112
  144. data/lib/schemacop/node.rb +0 -139
  145. data/lib/schemacop/node_supporting_field.rb +0 -58
  146. data/lib/schemacop/root_node.rb +0 -4
  147. data/lib/schemacop/validator/array_validator.rb +0 -30
  148. data/lib/schemacop/validator/float_validator.rb +0 -5
  149. data/lib/schemacop/validator/hash_validator.rb +0 -35
  150. data/lib/schemacop/validator/integer_validator.rb +0 -5
  151. data/lib/schemacop/validator/number_validator.rb +0 -19
  152. data/lib/schemacop/validator/object_validator.rb +0 -27
  153. data/lib/schemacop/validator/string_validator.rb +0 -37
  154. data/test/casting_test.rb +0 -100
  155. data/test/collector_test.rb +0 -45
  156. data/test/custom_check_test.rb +0 -93
  157. data/test/custom_if_test.rb +0 -95
  158. data/test/defaults_test.rb +0 -93
  159. data/test/empty_test.rb +0 -14
  160. data/test/nil_dis_allow_test.rb +0 -41
  161. data/test/node_resolver_test.rb +0 -26
  162. data/test/short_forms_test.rb +0 -349
  163. data/test/test_helper.rb +0 -13
  164. data/test/types_test.rb +0 -84
  165. data/test/validator_array_test.rb +0 -97
  166. data/test/validator_boolean_test.rb +0 -15
  167. data/test/validator_float_test.rb +0 -57
  168. data/test/validator_hash_test.rb +0 -93
  169. data/test/validator_integer_test.rb +0 -46
  170. data/test/validator_nil_test.rb +0 -13
  171. data/test/validator_number_test.rb +0 -60
  172. data/test/validator_object_test.rb +0 -139
  173. data/test/validator_string_test.rb +0 -76
  174. data/test/validator_symbol_test.rb +0 -16
@@ -0,0 +1,126 @@
1
+ require 'test_helper'
2
+ # rubocop:disable Lint/BooleanSymbol
3
+
4
+ module Schemacop
5
+ module V3
6
+ class BooleanNodeTest < V3Test
7
+ EXP_INVALID_TYPE = 'Invalid type, expected "boolean".'.freeze
8
+
9
+ def test_basic
10
+ schema :boolean
11
+
12
+ assert_validation true
13
+ assert_validation false
14
+
15
+ assert_json(type: :boolean)
16
+ end
17
+
18
+ def test_required
19
+ schema :boolean, required: true
20
+
21
+ assert_validation true
22
+ assert_validation false
23
+ assert_validation nil do
24
+ error '/', 'Value must be given.'
25
+ end
26
+
27
+ assert_json(type: :boolean)
28
+ end
29
+
30
+ def test_hash
31
+ schema { boo! :alive }
32
+ assert_json(
33
+ type: :object,
34
+ properties: {
35
+ alive: { type: :boolean }
36
+ },
37
+ required: %i[alive],
38
+ additionalProperties: false
39
+ )
40
+ assert_validation alive: true
41
+ assert_validation alive: false
42
+ end
43
+
44
+ def test_type
45
+ schema :boolean
46
+
47
+ assert_json(type: :boolean)
48
+
49
+ assert_validation 42 do
50
+ error '/', EXP_INVALID_TYPE
51
+ end
52
+
53
+ [:true, 'true', :false, 'false', 0, 1].each do |value|
54
+ assert_validation value do
55
+ error '/', EXP_INVALID_TYPE
56
+ end
57
+ end
58
+
59
+ schema { boo? :name }
60
+
61
+ assert_json(
62
+ type: :object,
63
+ properties: {
64
+ name: { type: :boolean }
65
+ },
66
+ additionalProperties: false
67
+ )
68
+
69
+ [:true, 'true', :false, 'false', 0, 1].each do |value|
70
+ assert_validation name: value do
71
+ error '/name', EXP_INVALID_TYPE
72
+ end
73
+ end
74
+ end
75
+
76
+ def test_enum_schema
77
+ schema :boolean, enum: [1, 2, 'foo', :bar, { qux: 42 }, true]
78
+
79
+ assert_json({
80
+ type: :boolean,
81
+ enum: [1, 2, 'foo', :bar, { qux: 42 }, true]
82
+ })
83
+
84
+ assert_validation(nil)
85
+ assert_validation(true)
86
+
87
+ # Even we put those types in the enum, they need to fail the validations,
88
+ # as they are not booleans
89
+ assert_validation('foo') do
90
+ error '/', 'Invalid type, expected "boolean".'
91
+ end
92
+ assert_validation(:bar) do
93
+ error '/', 'Invalid type, expected "boolean".'
94
+ end
95
+ assert_validation({ qux: 42 }) do
96
+ error '/', 'Invalid type, expected "boolean".'
97
+ end
98
+
99
+ # These need to fail validation, as they are not in the enum list
100
+ assert_validation(false) do
101
+ error '/', 'Value not included in enum [1, 2, "foo", :bar, {:qux=>42}, true].'
102
+ end
103
+ end
104
+
105
+ def test_with_generic_keywords
106
+ schema :boolean, enum: [1, 'foo', true],
107
+ title: 'Boolean schema',
108
+ description: 'Boolean schema holding generic keywords',
109
+ examples: [
110
+ true
111
+ ]
112
+
113
+ assert_json({
114
+ type: :boolean,
115
+ enum: [1, 'foo', true],
116
+ title: 'Boolean schema',
117
+ description: 'Boolean schema holding generic keywords',
118
+ examples: [
119
+ true
120
+ ]
121
+ })
122
+ end
123
+ end
124
+ end
125
+ end
126
+ # rubocop:enable Lint/BooleanSymbol
@@ -0,0 +1,164 @@
1
+ require 'test_helper'
2
+
3
+ module Schemacop
4
+ module V3
5
+ class GlobalContextTest < V3Test
6
+ def setup
7
+ super
8
+ GlobalContext.instance_variable_set(:@instance, GlobalContext.send(:new))
9
+ Schemacop.load_paths = ['test/schemas']
10
+ end
11
+
12
+ def test_instance
13
+ assert_equal GlobalContext.instance, GlobalContext.instance
14
+ end
15
+
16
+ def test_eager_load
17
+ refute GlobalContext.instance.instance_variable_get(:@eager_loaded)
18
+ GlobalContext.instance.eager_load!
19
+ assert GlobalContext.instance.instance_variable_get(:@eager_loaded)
20
+
21
+ assert_raises_with_message RuntimeError, /can't be eager loaded more than once/ do
22
+ GlobalContext.instance.eager_load!
23
+ end
24
+ end
25
+
26
+ def test_schemas
27
+ assert_equal({}, GlobalContext.instance.schemas)
28
+ GlobalContext.instance.eager_load!
29
+ assert_equal(%i[nested/group user], GlobalContext.instance.schemas.keys)
30
+ assert_is_a Node, GlobalContext.instance.schemas.values.first
31
+ end
32
+
33
+ def test_schema_for_w_eager_loading
34
+ GlobalContext.instance.eager_load!
35
+
36
+ 2.times do
37
+ assert_is_a HashNode, GlobalContext.instance.schema_for('user')
38
+ assert_equal(%i[nested/group user], GlobalContext.instance.schemas.keys)
39
+ assert_is_a HashNode, GlobalContext.instance.schema_for('user')
40
+ end
41
+ end
42
+
43
+ def test_schema_for_wo_eager_loading
44
+ assert_is_a HashNode, GlobalContext.instance.schema_for('user')
45
+ assert_equal(%i[user], GlobalContext.instance.schemas.keys)
46
+ assert_is_a HashNode, GlobalContext.instance.schema_for('user')
47
+ assert_is_a HashNode, GlobalContext.instance.schema_for('nested/group')
48
+ assert_equal(%i[user nested/group], GlobalContext.instance.schemas.keys)
49
+ end
50
+
51
+ def test_file_reload
52
+ dir = Dir.mktmpdir
53
+ Schemacop.load_paths << dir
54
+ IO.write(File.join(dir, 'foo.rb'), %(schema :string))
55
+ assert_is_a StringNode, GlobalContext.instance.schema_for('foo')
56
+ IO.write(File.join(dir, 'foo.rb'), %(schema :integer))
57
+ assert_is_a IntegerNode, GlobalContext.instance.schema_for('foo')
58
+ end
59
+
60
+ def test_file_not_reloaded_in_eager
61
+ dir = Dir.mktmpdir
62
+ Schemacop.load_paths << dir
63
+
64
+ IO.write(File.join(dir, 'foo.rb'), %(schema :string))
65
+
66
+ GlobalContext.instance.eager_load!
67
+
68
+ assert_is_a StringNode, GlobalContext.instance.schema_for('foo')
69
+ IO.write(File.join(dir, 'foo.rb'), %(schema :integer))
70
+ assert_is_a StringNode, GlobalContext.instance.schema_for('foo')
71
+ end
72
+
73
+ def test_schema_not_found
74
+ assert_nil GlobalContext.instance.schema_for('foo')
75
+ GlobalContext.instance.eager_load!
76
+ assert_nil GlobalContext.instance.schema_for('foo')
77
+ end
78
+
79
+ def test_inter_references
80
+ schema = GlobalContext.instance.schema_for('user')
81
+
82
+ assert schema.validate(
83
+ first_name: 'John',
84
+ last_name: 'Doe',
85
+ groups: [
86
+ { name: 'Group 1' }
87
+ ]
88
+ ).valid?
89
+
90
+ refute schema.validate(
91
+ first_name: 'John',
92
+ last_name: 'Doe',
93
+ groups: [
94
+ { name_x: 'Group 1' }
95
+ ]
96
+ ).valid?
97
+
98
+ schema = GlobalContext.instance.schema_for('nested/group')
99
+
100
+ assert schema.validate(
101
+ name: 'Group 1',
102
+ users: [
103
+ { first_name: 'John', last_name: 'Doe' }
104
+ ]
105
+ ).valid?
106
+
107
+ refute schema.validate(
108
+ name: 'Group 1',
109
+ users: [
110
+ { first_name_x: 'John', last_name: 'Doe' }
111
+ ]
112
+ ).valid?
113
+ end
114
+
115
+ def test_empty_schema
116
+ dir = Dir.mktmpdir
117
+ Schemacop.load_paths << dir
118
+ IO.write(File.join(dir, 'foo.rb'), %())
119
+ assert_raises_with_message RuntimeError, /does not define any schema/ do
120
+ GlobalContext.instance.schema_for('foo')
121
+ end
122
+ end
123
+
124
+ def test_multiple_schemas
125
+ dir = Dir.mktmpdir
126
+ Schemacop.load_paths << dir
127
+ IO.write(File.join(dir, 'foo.rb'), %(schema :string\nschema :integer))
128
+ assert_raises_with_message RuntimeError, /Schema "#{File.join(dir, 'foo.rb')}" defines multiple schemas/ do
129
+ GlobalContext.instance.schema_for('foo')
130
+ end
131
+ end
132
+
133
+ def test_invalid_schema
134
+ dir = Dir.mktmpdir
135
+ Schemacop.load_paths << dir
136
+ IO.write(File.join(dir, 'foo.rb'), %(foobarbaz))
137
+
138
+ assert_raises_with_message RuntimeError, /Could not load schema/ do
139
+ GlobalContext.schema_for('foo')
140
+ end
141
+ end
142
+
143
+ def test_overrides_with_eager_load
144
+ dir = Dir.mktmpdir
145
+ Schemacop.load_paths << dir
146
+ IO.write(File.join(dir, 'user.rb'), %(schema :string))
147
+
148
+ assert_raises_with_message RuntimeError, %r{in both load paths "test/schemas" and "#{dir}"} do
149
+ GlobalContext.eager_load!
150
+ end
151
+ end
152
+
153
+ def test_overrides_with_lazy_load
154
+ dir = Dir.mktmpdir
155
+ Schemacop.load_paths << dir
156
+ IO.write(File.join(dir, 'user.rb'), %(schema :string))
157
+
158
+ assert_raises_with_message RuntimeError, %r{in both load paths "test/schemas" and "#{dir}"} do
159
+ GlobalContext.instance.schema_for('user')
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,884 @@
1
+ require 'test_helper'
2
+
3
+ module Schemacop
4
+ module V3
5
+ class HashNodeTest < V3Test
6
+ EXP_INVALID_TYPE = 'Invalid type, expected "hash".'.freeze
7
+
8
+ def test_basic
9
+ schema
10
+ assert_validation({})
11
+ assert_json(type: :object, additionalProperties: false)
12
+
13
+ schema :hash
14
+ assert_validation({})
15
+
16
+ assert_json(type: :object, additionalProperties: false)
17
+ end
18
+
19
+ def test_additional_properties_false
20
+ schema
21
+ assert_validation({})
22
+ assert_validation(foo: :bar, bar: :baz) do
23
+ error '/', 'Obsolete property "foo".'
24
+ error '/', 'Obsolete property "bar".'
25
+ end
26
+ assert_json(type: :object, additionalProperties: false)
27
+ end
28
+
29
+ def test_additional_properties_true
30
+ schema :hash, additional_properties: true
31
+ assert_validation({})
32
+ assert_validation(foo: :bar)
33
+ assert_validation(foo: { bar: :baz })
34
+
35
+ assert_json(type: :object, additionalProperties: true)
36
+ end
37
+
38
+ def test_additional_properties_schema
39
+ schema :hash do
40
+ str! :foo
41
+ add :string
42
+ end
43
+
44
+ assert_validation(foo: 'bar', baz: 'foo', answer: '42')
45
+ assert_validation(foo: 'bar', baz: 'foo', answer: 42) do
46
+ error '/answer', 'Invalid type, expected "string".'
47
+ end
48
+
49
+ assert_json(
50
+ properties: {
51
+ foo: { type: :string }
52
+ },
53
+ required: %i[foo],
54
+ type: :object,
55
+ additionalProperties: { type: :string }
56
+ )
57
+
58
+ assert_nothing_raised do
59
+ @schema.validate!({ foo: 'foo' })
60
+ end
61
+
62
+ assert_raises_with_message Exceptions::ValidationError, '/bar: Invalid type, expected "string".' do
63
+ @schema.validate!({ foo: 'foo', bar: :baz })
64
+ end
65
+ end
66
+
67
+ def test_property_names
68
+ schema :hash, additional_properties: true, property_names: '^[a-zA-Z0-9]+$'
69
+ assert_validation({})
70
+ assert_validation(foo: :bar)
71
+ assert_validation('foo' => 'bar')
72
+ assert_validation(Foo: :bar)
73
+ assert_validation('_foo39sjfdoi 345893(%' => 'bar', 'foo' => 'bar') do
74
+ error '/', 'Property name "_foo39sjfdoi 345893(%" does not match "^[a-zA-Z0-9]+$".'
75
+ end
76
+
77
+ assert_json(
78
+ type: :object,
79
+ additionalProperties: true,
80
+ propertyNames: '^[a-zA-Z0-9]+$'
81
+ )
82
+
83
+ assert_cast({ foo: 123 }, { foo: 123 }.with_indifferent_access)
84
+ assert_cast({ Foo: 123 }, { Foo: 123 }.with_indifferent_access)
85
+
86
+ # New schema
87
+ schema :hash, additional_properties: true, property_names: '^[a-z]+$'
88
+
89
+ assert_validation({})
90
+ assert_validation(foo: :bar)
91
+ assert_validation('foo' => 'bar')
92
+ assert_validation(Foo: :bar) do
93
+ error '/', 'Property name "Foo" does not match "^[a-z]+$".'
94
+ end
95
+ assert_validation('_foo39sjfdoi 345893(%' => 'bar', 'foo' => 'bar') do
96
+ error '/', 'Property name "_foo39sjfdoi 345893(%" does not match "^[a-z]+$".'
97
+ end
98
+
99
+ assert_cast({ foo: 123 }, { foo: 123 }.with_indifferent_access)
100
+ end
101
+
102
+ def test_required
103
+ schema do
104
+ str! :foo
105
+ int? :bar
106
+ end
107
+
108
+ assert_validation(foo: 'sdfsd')
109
+ assert_validation(foo: 'sdfsd', bar: 42)
110
+
111
+ assert_validation(bar: 42) do
112
+ error '/foo', 'Value must be given.'
113
+ end
114
+
115
+ assert_validation({}) do
116
+ error '/foo', 'Value must be given.'
117
+ end
118
+
119
+ assert_json(
120
+ type: :object,
121
+ properties: {
122
+ foo: { type: :string },
123
+ bar: { type: :integer }
124
+ },
125
+ required: %i[foo],
126
+ additionalProperties: false
127
+ )
128
+ end
129
+
130
+ def test_min_properties
131
+ schema :hash, min_properties: 2, additional_properties: true
132
+ assert_validation(foo: :bar, bar: :baz)
133
+ assert_validation(foo: :bar, bar: :baz, baz: :foo)
134
+
135
+ assert_validation(foo: :bar) do
136
+ error '/', 'Has 1 properties but needs at least 2.'
137
+ end
138
+
139
+ assert_validation({}) do
140
+ error '/', 'Has 0 properties but needs at least 2.'
141
+ end
142
+
143
+ assert_json(
144
+ type: :object,
145
+ minProperties: 2,
146
+ additionalProperties: true
147
+ )
148
+ end
149
+
150
+ def test_max_properties
151
+ schema :hash, max_properties: 3, additional_properties: true
152
+ assert_validation(foo: :bar, bar: :baz)
153
+ assert_validation(foo: :bar, bar: :baz, baz: :foo)
154
+
155
+ assert_validation(foo: :bar, bar: :baz, baz: :foo, answer: 42) do
156
+ error '/', 'Has 4 properties but needs at most 3.'
157
+ end
158
+
159
+ assert_json(
160
+ type: :object,
161
+ maxProperties: 3,
162
+ additionalProperties: true
163
+ )
164
+ end
165
+
166
+ def test_min_max_properties
167
+ schema :hash, min_properties: 3, max_properties: 3, additional_properties: true
168
+ assert_validation(foo: :bar, bar: :baz, baz: :foo)
169
+
170
+ assert_validation(foo: :bar, bar: :baz, baz: :foo, answer: 42) do
171
+ error '/', 'Has 4 properties but needs at most 3.'
172
+ end
173
+
174
+ assert_validation(foo: :bar, bar: :baz) do
175
+ error '/', 'Has 2 properties but needs at least 3.'
176
+ end
177
+
178
+ assert_json(
179
+ type: :object,
180
+ minProperties: 3,
181
+ maxProperties: 3,
182
+ additionalProperties: true
183
+ )
184
+ end
185
+
186
+ def test_dependencies
187
+ schema :hash do
188
+ str! :name
189
+ str? :credit_card
190
+ str? :billing_address
191
+ str? :phone_number
192
+
193
+ dep :credit_card, :billing_address, :phone_number
194
+ dep :billing_address, :credit_card
195
+ end
196
+
197
+ assert_validation(name: 'John')
198
+ assert_validation(name: 'John', credit_card: '23423523', billing_address: 'Example 3', phone_number: '234')
199
+
200
+ assert_validation(name: 'John', credit_card: '23423523') do
201
+ error '/', 'Missing property "billing_address" because "credit_card" is given.'
202
+ error '/', 'Missing property "phone_number" because "credit_card" is given.'
203
+ end
204
+
205
+ assert_validation(name: 'John', billing_address: 'Example 3') do
206
+ error '/', 'Missing property "credit_card" because "billing_address" is given.'
207
+ end
208
+
209
+ assert_json(
210
+ type: :object,
211
+ properties: {
212
+ name: { type: :string },
213
+ credit_card: { type: :string },
214
+ billing_address: { type: :string },
215
+ phone_number: { type: :string }
216
+ },
217
+ required: %i[name],
218
+ dependencies: {
219
+ credit_card: %i[billing_address phone_number],
220
+ billing_address: %i[credit_card]
221
+ },
222
+ additionalProperties: false
223
+ )
224
+ end
225
+
226
+ def test_pattern_properties_wo_additional
227
+ schema additional_properties: false do
228
+ str! :name
229
+ str?(/^foo_.*$/)
230
+ int?(/^bar_.*$/)
231
+ end
232
+
233
+ assert_validation(name: 'John', foo_bar: 'John')
234
+ assert_validation(name: 'John', foo_bar: 'John', bar_baz: 42, foo_baz: '42')
235
+ assert_validation(name: 'John', foo_baz: 'John', bar_baz: 42)
236
+
237
+ assert_validation(name: 'John', xy: 'John', bar_baz: 'Doe') do
238
+ error '/', 'Obsolete property "xy".'
239
+ error '/bar_baz', 'Invalid type, expected "integer".'
240
+ end
241
+
242
+ assert_json(
243
+ type: :object,
244
+ properties: {
245
+ name: { type: :string }
246
+ },
247
+ patternProperties: {
248
+ '^foo_.*$': { type: :string },
249
+ '^bar_.*$': { type: :integer }
250
+ },
251
+ additionalProperties: false,
252
+ required: %i[name]
253
+ )
254
+ end
255
+
256
+ def test_pattern_properties_w_additional
257
+ schema additional_properties: true do
258
+ int? :builtin
259
+ str?(/^S_/)
260
+ int?(/^I_/)
261
+ add :string
262
+ end
263
+
264
+ assert_validation(builtin: 42)
265
+ assert_validation(keyword: 'value')
266
+
267
+ assert_validation(keyword: 42) do
268
+ error '/keyword', 'Invalid type, expected "string".'
269
+ end
270
+
271
+ assert_json(
272
+ type: 'object',
273
+ properties: {
274
+ builtin: { type: :integer }
275
+ },
276
+ patternProperties: {
277
+ '^S_': { type: :string },
278
+ '^I_': { type: :integer }
279
+ },
280
+ additionalProperties: { type: :string }
281
+ )
282
+ end
283
+
284
+ def test_pattern_properties_casting
285
+ schema do
286
+ int?(/^id_.*$/)
287
+ int?(/^val.*$/)
288
+ end
289
+
290
+ assert_json({
291
+ type: :object,
292
+ patternProperties: {
293
+ '^id_.*$': { type: :integer },
294
+ '^val.*$': { type: :integer }
295
+ },
296
+ additionalProperties: false
297
+ })
298
+
299
+ assert_validation({})
300
+ assert_validation({ id_foo: 1 })
301
+ assert_validation({ id_foo: 1, id_bar: 2 })
302
+ assert_validation({ id_foo: 1, id_bar: 2, value: 4 })
303
+
304
+ assert_cast({ id_foo: 1 }, { id_foo: 1 }.with_indifferent_access)
305
+ assert_cast({ id_foo: 1, id_bar: 2 }, { id_foo: 1, id_bar: 2 }.with_indifferent_access)
306
+ assert_cast({ id_foo: 1, id_bar: 2, value: 4 }, { id_foo: 1, id_bar: 2, value: 4 }.with_indifferent_access)
307
+ end
308
+
309
+ def test_defaults
310
+ schema do
311
+ str? :first_name, default: 'John'
312
+ str? :last_name, default: 'Doe'
313
+ str! :active, format: :boolean
314
+ hsh? :address, default: {} do
315
+ str? :street, default: 'Example 42'
316
+ end
317
+ end
318
+
319
+ data = { last_name: 'Doeringer', active: 'true' }
320
+ data_was = data.dup
321
+
322
+ expected_data = { first_name: 'John', last_name: 'Doeringer', active: true, address: { street: 'Example 42' } }.with_indifferent_access
323
+
324
+ assert_equal(expected_data, @schema.validate(data).data)
325
+ assert_equal data_was, data
326
+
327
+ schema do
328
+ hsh? :address do
329
+ str? :street, default: 'Example 42'
330
+ end
331
+ end
332
+
333
+ assert_equal({}, @schema.validate({}).data)
334
+ end
335
+
336
+ def test_all_of
337
+ schema do
338
+ all_of! :str do
339
+ str min_length: 3
340
+ str max_length: 5
341
+ end
342
+ end
343
+
344
+ assert_validation(str: '123')
345
+ assert_validation(str: '1234')
346
+ assert_validation(str: '12345')
347
+ assert_validation(str: '0') do
348
+ error '/str', 'Does not match all allOf conditions.'
349
+ end
350
+ end
351
+
352
+ def test_one_of_required
353
+ schema do
354
+ one_of! :str do
355
+ str min_length: 4
356
+ str min_length: 0, max_length: 4
357
+ end
358
+ end
359
+
360
+ assert_validation(str: '12345')
361
+ assert_validation(str: '123')
362
+ assert_validation(str: nil) do
363
+ error '/str', 'Value must be given.'
364
+ end
365
+ assert_validation(str: '1234') do
366
+ error '/str', 'Matches 2 definitions but should match exactly 1.'
367
+ end
368
+ end
369
+
370
+ def test_one_of_optional
371
+ schema do
372
+ one_of? :str do
373
+ str min_length: 4
374
+ str min_length: 0, max_length: 4
375
+ end
376
+ end
377
+
378
+ assert_validation(str: '12345')
379
+ assert_validation(str: '123')
380
+ assert_validation(str: nil)
381
+ assert_validation({})
382
+ assert_validation(str: '1234') do
383
+ error '/str', 'Matches 2 definitions but should match exactly 1.'
384
+ end
385
+ end
386
+
387
+ def test_any_of_required
388
+ schema do
389
+ any_of! :str_or_int do
390
+ str
391
+ int
392
+ end
393
+ end
394
+
395
+ assert_validation(str_or_int: 'Hello World')
396
+ assert_validation(str_or_int: 42)
397
+ assert_validation(str_or_int: :foo) do
398
+ error '/str_or_int', 'Does not match any anyOf condition.'
399
+ end
400
+ end
401
+
402
+ def test_any_of_optional
403
+ schema do
404
+ any_of? :str_or_int do
405
+ str
406
+ int
407
+ end
408
+ end
409
+
410
+ assert_validation(str_or_int: 'Hello World')
411
+ assert_validation(str_or_int: 42)
412
+ assert_validation(str_or_int: nil)
413
+ assert_validation({})
414
+ assert_validation(str_or_int: :foo) do
415
+ error '/str_or_int', 'Does not match any anyOf condition.'
416
+ end
417
+ end
418
+
419
+ def test_is_not_required
420
+ schema do
421
+ is_not! :foo, required: true do
422
+ str
423
+ end
424
+ end
425
+
426
+ assert_validation(foo: 42)
427
+ assert_validation(foo: true)
428
+ assert_validation(foo: { bar: :baz })
429
+ assert_validation(foo: nil) do
430
+ error '/foo', 'Value must be given.'
431
+ end
432
+ assert_validation(foo: 'string') do
433
+ error '/foo', 'Must not match schema: {"type"=>"string"}.'
434
+ end
435
+ end
436
+
437
+ def test_is_not_optional
438
+ schema do
439
+ is_not? :foo do
440
+ str
441
+ end
442
+ end
443
+
444
+ assert_validation(foo: 42)
445
+ assert_validation(foo: true)
446
+ assert_validation(foo: { bar: :baz })
447
+ assert_validation(foo: nil)
448
+ assert_validation(foo: 'string') do
449
+ error '/foo', 'Must not match schema: {"type"=>"string"}.'
450
+ end
451
+ end
452
+
453
+ # Helper function that checks for all the options if the option is
454
+ # an integer or something else, in which case it needs to raise
455
+ def validate_self_should_error(value_to_check)
456
+ assert_raises_with_message Exceptions::InvalidSchemaError,
457
+ 'Option "min_properties" must be an "integer"' do
458
+ schema :hash, min_properties: value_to_check
459
+ end
460
+
461
+ assert_raises_with_message Exceptions::InvalidSchemaError,
462
+ 'Option "max_properties" must be an "integer"' do
463
+ schema :hash, max_properties: value_to_check
464
+ end
465
+ end
466
+
467
+ def test_validate_self
468
+ assert_raises_with_message Exceptions::InvalidSchemaError,
469
+ 'Pattern properties can\'t be required.' do
470
+ schema :hash do
471
+ str!(/[a-z]+/)
472
+ end
473
+ end
474
+
475
+ validate_self_should_error(1.0)
476
+ validate_self_should_error(4r)
477
+ validate_self_should_error(true)
478
+ validate_self_should_error(false)
479
+ validate_self_should_error((4 + 6i))
480
+ validate_self_should_error('13')
481
+ validate_self_should_error('Lorem ipsum')
482
+
483
+ # rubocop:disable Lint/BooleanSymbol
484
+ assert_raises_with_message Exceptions::InvalidSchemaError,
485
+ 'Option "additional_properties" must be a boolean value' do
486
+ schema :hash, additional_properties: :true
487
+ end
488
+ # rubocop:enable Lint/BooleanSymbol
489
+ end
490
+
491
+ def test_doc_example
492
+ schema :hash do
493
+ scm :address do
494
+ str! :street
495
+ int! :number
496
+ str! :zip
497
+ end
498
+ int? :id
499
+ str! :name
500
+ ref! :address, :address
501
+ ary! :additional_addresses, default: [] do
502
+ ref :address
503
+ end
504
+ ary? :comments, :array, default: [] do
505
+ str
506
+ end
507
+ hsh! :jobs, min_properties: 1 do
508
+ str?(/^[0-9]+$/)
509
+ end
510
+ end
511
+
512
+ assert_validation(
513
+ id: 42,
514
+ name: 'John Doe',
515
+ address: {
516
+ street: 'Silver Street',
517
+ number: 4,
518
+ zip: '38234C'
519
+ },
520
+ additional_addresses: [
521
+ { street: 'Example street', number: 42, zip: '8048' }
522
+ ],
523
+ comments: [
524
+ 'This is a comment'
525
+ ],
526
+ jobs: {
527
+ 2020 => 'Software Engineer'
528
+ }
529
+ )
530
+ end
531
+
532
+ def test_cast_without_additional
533
+ schema :hash do
534
+ str! :foo, format: :integer
535
+ end
536
+
537
+ assert_validation(nil)
538
+ assert_validation(foo: '1')
539
+ assert_cast({ foo: '1' }, { foo: 1 }.with_indifferent_access)
540
+
541
+ assert_validation(foo: '1', bar: '2') do
542
+ error '/', 'Obsolete property "bar".'
543
+ end
544
+
545
+ assert_json(
546
+ type: 'object',
547
+ properties: {
548
+ foo: {
549
+ type: :string,
550
+ format: :integer
551
+ }
552
+ },
553
+ additionalProperties: false,
554
+ required: %i[foo]
555
+ )
556
+ end
557
+
558
+ def test_cast_with_additional
559
+ schema :hash, additional_properties: true do
560
+ str! :foo, format: :integer
561
+ end
562
+
563
+ assert_validation(nil)
564
+ assert_validation(foo: '1')
565
+ assert_cast({ foo: '1' }, { foo: 1 }.with_indifferent_access)
566
+
567
+ assert_validation(foo: '1', bar: nil)
568
+ assert_validation(foo: '1', bar: '2')
569
+ assert_cast({ foo: '1', bar: '2' }, { foo: 1, bar: '2' }.with_indifferent_access)
570
+
571
+ assert_json(
572
+ type: 'object',
573
+ properties: {
574
+ foo: {
575
+ type: :string,
576
+ format: :integer
577
+ }
578
+ },
579
+ additionalProperties: true,
580
+ required: %i[foo]
581
+ )
582
+ end
583
+
584
+ def test_multiple_add_in_schema
585
+ assert_raises_with_message Exceptions::InvalidSchemaError,
586
+ 'You can only use "add" once to specify additional properties.' do
587
+ schema :hash do
588
+ add :integer
589
+ add :string
590
+ end
591
+ end
592
+ end
593
+
594
+ def test_cast_with_additional_in_block
595
+ schema :hash do
596
+ str! :foo, format: :integer
597
+ add :string
598
+ end
599
+
600
+ assert_validation(nil)
601
+ assert_validation(foo: '1')
602
+ assert_cast({ foo: '1' }, { foo: 1 }.with_indifferent_access)
603
+
604
+ assert_validation(foo: '1', bar: nil)
605
+ assert_validation(foo: '1', bar: '2')
606
+ assert_cast({ foo: '1', bar: '2' }, { foo: 1, bar: '2' }.with_indifferent_access)
607
+
608
+ assert_json(
609
+ type: 'object',
610
+ properties: {
611
+ foo: {
612
+ type: :string,
613
+ format: :integer
614
+ }
615
+ },
616
+ additionalProperties: { type: :string },
617
+ required: %i[foo]
618
+ )
619
+ end
620
+
621
+ def test_cast_with_additional_in_block_with_casting
622
+ schema :hash do
623
+ str! :foo, format: :integer
624
+ add :string, format: :integer
625
+ end
626
+
627
+ assert_validation(nil)
628
+ assert_validation(foo: '1')
629
+ assert_cast({ foo: '1' }, { foo: 1 }.with_indifferent_access)
630
+
631
+ assert_validation(foo: '1', bar: nil)
632
+ assert_validation(foo: '1', bar: '2')
633
+ assert_cast({ foo: '1', bar: '2' }, { foo: 1, bar: 2 }.with_indifferent_access)
634
+ end
635
+
636
+ def test_cast_with_additional_any_of
637
+ schema :hash do
638
+ str! :foo, format: :integer
639
+ add :any_of do
640
+ str
641
+ int
642
+ end
643
+ end
644
+
645
+ assert_validation(nil)
646
+ assert_validation(foo: '1')
647
+ assert_cast({ foo: '1' }, { foo: 1 }.with_indifferent_access)
648
+
649
+ assert_validation(foo: '1', bar: nil)
650
+ assert_validation(foo: '1', bar: '2')
651
+ assert_validation(foo: '1', bar: '2', baz: 3)
652
+ assert_validation(foo: '1', bar: '2', baz: 3, qux: [1, 2]) do
653
+ error '/qux', 'Does not match any anyOf condition.'
654
+ end
655
+
656
+ assert_cast({ foo: '1', bar: '2' }, { foo: 1, bar: '2' }.with_indifferent_access)
657
+
658
+ assert_json(
659
+ type: 'object',
660
+ properties: {
661
+ foo: {
662
+ type: :string,
663
+ format: :integer
664
+ }
665
+ },
666
+ additionalProperties: {
667
+ anyOf: [
668
+ { type: :string },
669
+ { type: :integer }
670
+ ]
671
+ },
672
+ required: %i[foo]
673
+ )
674
+ end
675
+
676
+ def test_cast_with_additional_any_of_with_casting
677
+ schema :hash do
678
+ str! :foo, format: :integer
679
+ add :any_of do
680
+ str format: :integer
681
+ str format: :date
682
+ int
683
+ end
684
+ end
685
+
686
+ assert_validation(nil)
687
+ assert_validation(foo: '1')
688
+ assert_cast({ foo: '1' }, { foo: 1 }.with_indifferent_access)
689
+
690
+ assert_validation(foo: '1', bar: nil)
691
+ assert_validation(foo: '1', bar: '2')
692
+ assert_validation(foo: '1', bar: '2', baz: 3)
693
+ assert_validation(foo: '1', bar: '2', baz: 3, qux: [1, 2]) do
694
+ error '/qux', 'Does not match any anyOf condition.'
695
+ end
696
+
697
+ assert_cast({ foo: '1', bar: '2' }, { foo: 1, bar: 2 }.with_indifferent_access)
698
+ assert_cast({ foo: '1', bar: '2', qux: '2020-01-13', asd: 1 }, { foo: 1, bar: 2, qux: Date.new(2020, 1, 13), asd: 1 }.with_indifferent_access)
699
+
700
+ assert_json(
701
+ type: 'object',
702
+ properties: {
703
+ foo: {
704
+ type: :string,
705
+ format: :integer
706
+ }
707
+ },
708
+ additionalProperties: {
709
+ anyOf: [
710
+ {
711
+ type: :string,
712
+ format: :integer
713
+ },
714
+ {
715
+ type: :string,
716
+ format: :date
717
+ },
718
+ {
719
+ type: :integer
720
+ }
721
+ ]
722
+ },
723
+ required: %i[foo]
724
+ )
725
+ end
726
+
727
+ def test_enum_schema
728
+ schema :hash do
729
+ str! :foo, enum: ['bar', 'qux', 123, :faz]
730
+ end
731
+
732
+ assert_json({
733
+ type: :object,
734
+ additionalProperties: false,
735
+ properties: {
736
+ foo: {
737
+ type: :string,
738
+ enum: ['bar', 'qux', 123, :faz]
739
+ }
740
+ },
741
+ required: [:foo]
742
+ })
743
+
744
+ assert_validation(nil)
745
+ assert_validation({ foo: 'bar' })
746
+ assert_validation({ foo: 'qux' })
747
+
748
+ # Even we put those types in the enum, they need to fail the validations,
749
+ # as they are not strings
750
+ assert_validation({ foo: 123 }) do
751
+ error '/foo', 'Invalid type, expected "string".'
752
+ end
753
+ assert_validation({ foo: :faz }) do
754
+ error '/foo', 'Invalid type, expected "string".'
755
+ end
756
+
757
+ # These need to fail validation, as they are not in the enum list
758
+ assert_validation({ foo: 'Lorem ipsum' }) do
759
+ error '/foo', 'Value not included in enum ["bar", "qux", 123, :faz].'
760
+ end
761
+ end
762
+
763
+ def test_with_generic_keywords
764
+ schema :hash, title: 'Hash', description: 'A hash with a description' do
765
+ str! :foo,
766
+ enum: ['bar', 'qux', 123, :faz],
767
+ title: 'A string',
768
+ description: 'A string in the hash',
769
+ examples: [
770
+ 'foo'
771
+ ]
772
+ end
773
+
774
+ assert_json({
775
+ type: :object,
776
+ additionalProperties: false,
777
+ title: 'Hash',
778
+ description: 'A hash with a description',
779
+ properties: {
780
+ foo: {
781
+ type: :string,
782
+ enum: ['bar', 'qux', 123, :faz],
783
+ title: 'A string',
784
+ examples: ['foo'],
785
+ description: 'A string in the hash'
786
+ }
787
+ },
788
+ required: [:foo]
789
+ })
790
+ end
791
+
792
+ def test_hash_with_indifferent_access
793
+ schema :hash do
794
+ str! :foo
795
+ int? :bar
796
+ add :symbol
797
+ end
798
+
799
+ # Test with symbol notation
800
+ hash = ActiveSupport::HashWithIndifferentAccess.new
801
+
802
+ assert_validation(hash) do
803
+ error '/foo', 'Value must be given.'
804
+ end
805
+ hash[:foo] = 'Foo'
806
+ assert_validation(hash)
807
+ hash[:bar] = 123
808
+ assert_validation(hash)
809
+ hash[:qux] = :ruby
810
+ assert_validation(hash)
811
+
812
+ # Test with string notation
813
+ hash = ActiveSupport::HashWithIndifferentAccess.new
814
+
815
+ assert_validation(hash) do
816
+ error '/foo', 'Value must be given.'
817
+ end
818
+ hash['foo'] = 'Foo'
819
+ assert_validation(hash)
820
+ hash['bar'] = 123
821
+ assert_validation(hash)
822
+ hash['qux'] = :ruby
823
+ assert_validation(hash)
824
+ end
825
+
826
+ def test_invalid_schema
827
+ assert_raises_with_message Exceptions::InvalidSchemaError,
828
+ 'Child nodes must have a name.' do
829
+ schema :hash do
830
+ int!
831
+ end
832
+ end
833
+ end
834
+
835
+ def test_schema_with_string_keys
836
+ schema :hash do
837
+ int! 'foo'
838
+ end
839
+
840
+ assert_validation(nil)
841
+ assert_validation({ 'foo' => 42 })
842
+ assert_validation({ foo: 42 })
843
+
844
+ assert_cast({ 'foo' => 42 }, { 'foo' => 42 })
845
+ assert_cast({ foo: 42 }, { foo: 42 }.with_indifferent_access)
846
+
847
+ assert_validation({}) do
848
+ error '/foo', 'Value must be given.'
849
+ end
850
+
851
+ assert_validation({ :foo => 42, 'foo' => 43 }) do
852
+ error '/', 'Has 1 ambiguous properties: [:foo].'
853
+ end
854
+ end
855
+
856
+ def test_schema_with_string_keys_in_data
857
+ schema :hash do
858
+ int! :foo
859
+ end
860
+
861
+ assert_validation(nil)
862
+ assert_validation({ 'foo' => 42 })
863
+ assert_validation({ foo: 42 })
864
+
865
+ assert_cast({ 'foo' => 42 }, { 'foo' => 42 })
866
+ assert_cast({ foo: 42 }, { foo: 42 }.with_indifferent_access)
867
+
868
+ assert_validation({}) do
869
+ error '/foo', 'Value must be given.'
870
+ end
871
+
872
+ assert_validation({ :foo => 42, 'foo' => 43 }) do
873
+ error '/', 'Has 1 ambiguous properties: [:foo].'
874
+ end
875
+ end
876
+
877
+ # def test_invalid_key_names
878
+ # schema :hash do
879
+ # int!
880
+ # end
881
+ # end
882
+ end
883
+ end
884
+ end