schemacop 2.4.7 → 3.0.0.rc4

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