schemacop 2.4.6 → 3.0.0.rc3

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 (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