grape 1.1.0 → 1.5.3

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 (286) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +278 -44
  3. data/LICENSE +1 -1
  4. data/README.md +514 -69
  5. data/UPGRADING.md +424 -17
  6. data/grape.gemspec +13 -2
  7. data/lib/grape.rb +104 -71
  8. data/lib/grape/api.rb +138 -175
  9. data/lib/grape/api/helpers.rb +2 -0
  10. data/lib/grape/api/instance.rb +283 -0
  11. data/lib/grape/config.rb +34 -0
  12. data/lib/grape/content_types.rb +34 -0
  13. data/lib/grape/cookies.rb +2 -0
  14. data/lib/grape/dsl/api.rb +2 -0
  15. data/lib/grape/dsl/callbacks.rb +22 -0
  16. data/lib/grape/dsl/configuration.rb +2 -0
  17. data/lib/grape/dsl/desc.rb +41 -7
  18. data/lib/grape/dsl/headers.rb +2 -0
  19. data/lib/grape/dsl/helpers.rb +5 -2
  20. data/lib/grape/dsl/inside_route.rb +92 -49
  21. data/lib/grape/dsl/logger.rb +2 -0
  22. data/lib/grape/dsl/middleware.rb +9 -0
  23. data/lib/grape/dsl/parameters.rb +25 -14
  24. data/lib/grape/dsl/request_response.rb +4 -2
  25. data/lib/grape/dsl/routing.rb +17 -10
  26. data/lib/grape/dsl/settings.rb +7 -1
  27. data/lib/grape/dsl/validations.rb +24 -4
  28. data/lib/grape/eager_load.rb +20 -0
  29. data/lib/grape/endpoint.rb +59 -35
  30. data/lib/grape/error_formatter.rb +4 -2
  31. data/lib/grape/error_formatter/base.rb +2 -0
  32. data/lib/grape/error_formatter/json.rb +2 -0
  33. data/lib/grape/error_formatter/txt.rb +2 -0
  34. data/lib/grape/error_formatter/xml.rb +2 -0
  35. data/lib/grape/exceptions/base.rb +20 -14
  36. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  37. data/lib/grape/exceptions/incompatible_option_values.rb +2 -0
  38. data/lib/grape/exceptions/invalid_accept_header.rb +2 -0
  39. data/lib/grape/exceptions/invalid_formatter.rb +2 -0
  40. data/lib/grape/exceptions/invalid_message_body.rb +2 -0
  41. data/lib/grape/exceptions/invalid_response.rb +11 -0
  42. data/lib/grape/exceptions/invalid_version_header.rb +2 -0
  43. data/lib/grape/exceptions/invalid_versioner_option.rb +2 -0
  44. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -0
  45. data/lib/grape/exceptions/method_not_allowed.rb +2 -0
  46. data/lib/grape/exceptions/missing_group_type.rb +2 -0
  47. data/lib/grape/exceptions/missing_mime_type.rb +2 -0
  48. data/lib/grape/exceptions/missing_option.rb +2 -0
  49. data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
  50. data/lib/grape/exceptions/unknown_options.rb +2 -0
  51. data/lib/grape/exceptions/unknown_parameter.rb +2 -0
  52. data/lib/grape/exceptions/unknown_validator.rb +2 -0
  53. data/lib/grape/exceptions/unsupported_group_type.rb +2 -0
  54. data/lib/grape/exceptions/validation.rb +4 -2
  55. data/lib/grape/exceptions/validation_array_errors.rb +2 -0
  56. data/lib/grape/exceptions/validation_errors.rb +16 -13
  57. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
  58. data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
  59. data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
  60. data/lib/grape/extensions/hash.rb +2 -0
  61. data/lib/grape/extensions/hashie/mash.rb +2 -0
  62. data/lib/grape/formatter.rb +5 -3
  63. data/lib/grape/formatter/json.rb +2 -0
  64. data/lib/grape/formatter/serializable_hash.rb +2 -0
  65. data/lib/grape/formatter/txt.rb +2 -0
  66. data/lib/grape/formatter/xml.rb +2 -0
  67. data/lib/grape/http/headers.rb +50 -18
  68. data/lib/grape/locale/en.yml +3 -1
  69. data/lib/grape/middleware/auth/base.rb +7 -7
  70. data/lib/grape/middleware/auth/dsl.rb +2 -0
  71. data/lib/grape/middleware/auth/strategies.rb +2 -0
  72. data/lib/grape/middleware/auth/strategy_info.rb +2 -0
  73. data/lib/grape/middleware/base.rb +10 -7
  74. data/lib/grape/middleware/error.rb +21 -16
  75. data/lib/grape/middleware/filter.rb +2 -0
  76. data/lib/grape/middleware/formatter.rb +8 -6
  77. data/lib/grape/middleware/globals.rb +2 -0
  78. data/lib/grape/middleware/helpers.rb +12 -0
  79. data/lib/grape/middleware/stack.rb +13 -3
  80. data/lib/grape/middleware/versioner.rb +2 -0
  81. data/lib/grape/middleware/versioner/accept_version_header.rb +2 -0
  82. data/lib/grape/middleware/versioner/header.rb +10 -8
  83. data/lib/grape/middleware/versioner/param.rb +3 -1
  84. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +4 -1
  85. data/lib/grape/middleware/versioner/path.rb +3 -1
  86. data/lib/grape/namespace.rb +14 -2
  87. data/lib/grape/parser.rb +4 -2
  88. data/lib/grape/parser/json.rb +3 -1
  89. data/lib/grape/parser/xml.rb +3 -1
  90. data/lib/grape/path.rb +15 -3
  91. data/lib/grape/presenters/presenter.rb +2 -0
  92. data/lib/grape/request.rb +19 -10
  93. data/lib/grape/router.rb +30 -29
  94. data/lib/grape/router/attribute_translator.rb +41 -8
  95. data/lib/grape/router/pattern.rb +20 -16
  96. data/lib/grape/router/route.rb +14 -28
  97. data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
  98. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
  99. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
  100. data/lib/grape/util/base_inheritable.rb +43 -0
  101. data/lib/grape/util/cache.rb +20 -0
  102. data/lib/grape/util/endpoint_configuration.rb +8 -0
  103. data/lib/grape/util/env.rb +19 -17
  104. data/lib/grape/util/inheritable_setting.rb +2 -0
  105. data/lib/grape/util/inheritable_values.rb +7 -25
  106. data/lib/grape/util/json.rb +2 -0
  107. data/lib/grape/util/lazy_block.rb +27 -0
  108. data/lib/grape/util/lazy_object.rb +43 -0
  109. data/lib/grape/util/lazy_value.rb +98 -0
  110. data/lib/grape/util/registrable.rb +2 -0
  111. data/lib/grape/util/reverse_stackable_values.rb +10 -35
  112. data/lib/grape/util/stackable_values.rb +21 -34
  113. data/lib/grape/util/strict_hash_configuration.rb +2 -0
  114. data/lib/grape/util/xml.rb +2 -0
  115. data/lib/grape/validations.rb +2 -0
  116. data/lib/grape/validations/attributes_iterator.rb +16 -6
  117. data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
  118. data/lib/grape/validations/params_scope.rb +51 -30
  119. data/lib/grape/validations/single_attribute_iterator.rb +24 -0
  120. data/lib/grape/validations/types.rb +13 -38
  121. data/lib/grape/validations/types/array_coercer.rb +65 -0
  122. data/lib/grape/validations/types/build_coercer.rb +47 -49
  123. data/lib/grape/validations/types/custom_type_coercer.rb +29 -51
  124. data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
  125. data/lib/grape/validations/types/dry_type_coercer.rb +76 -0
  126. data/lib/grape/validations/types/file.rb +22 -18
  127. data/lib/grape/validations/types/invalid_value.rb +24 -0
  128. data/lib/grape/validations/types/json.rb +46 -39
  129. data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
  130. data/lib/grape/validations/types/primitive_coercer.rb +67 -0
  131. data/lib/grape/validations/types/set_coercer.rb +40 -0
  132. data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
  133. data/lib/grape/validations/validator_factory.rb +8 -11
  134. data/lib/grape/validations/validators/all_or_none.rb +8 -13
  135. data/lib/grape/validations/validators/allow_blank.rb +3 -1
  136. data/lib/grape/validations/validators/as.rb +5 -4
  137. data/lib/grape/validations/validators/at_least_one_of.rb +7 -13
  138. data/lib/grape/validations/validators/base.rb +20 -16
  139. data/lib/grape/validations/validators/coerce.rb +46 -29
  140. data/lib/grape/validations/validators/default.rb +6 -6
  141. data/lib/grape/validations/validators/exactly_one_of.rb +10 -23
  142. data/lib/grape/validations/validators/except_values.rb +4 -2
  143. data/lib/grape/validations/validators/multiple_params_base.rb +17 -10
  144. data/lib/grape/validations/validators/mutual_exclusion.rb +8 -18
  145. data/lib/grape/validations/validators/presence.rb +3 -1
  146. data/lib/grape/validations/validators/regexp.rb +4 -2
  147. data/lib/grape/validations/validators/same_as.rb +26 -0
  148. data/lib/grape/validations/validators/values.rb +18 -6
  149. data/lib/grape/version.rb +3 -1
  150. data/spec/grape/api/custom_validations_spec.rb +5 -3
  151. data/spec/grape/api/deeply_included_options_spec.rb +2 -0
  152. data/spec/grape/api/defines_boolean_in_params_spec.rb +39 -0
  153. data/spec/grape/api/inherited_helpers_spec.rb +2 -0
  154. data/spec/grape/api/instance_spec.rb +104 -0
  155. data/spec/grape/api/invalid_format_spec.rb +2 -0
  156. data/spec/grape/api/namespace_parameters_in_route_spec.rb +2 -0
  157. data/spec/grape/api/nested_helpers_spec.rb +2 -0
  158. data/spec/grape/api/optional_parameters_in_route_spec.rb +2 -0
  159. data/spec/grape/api/parameters_modification_spec.rb +3 -1
  160. data/spec/grape/api/patch_method_helpers_spec.rb +2 -0
  161. data/spec/grape/api/recognize_path_spec.rb +2 -0
  162. data/spec/grape/api/required_parameters_in_route_spec.rb +2 -0
  163. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +2 -0
  164. data/spec/grape/api/routes_with_requirements_spec.rb +61 -0
  165. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +2 -0
  166. data/spec/grape/api/shared_helpers_spec.rb +2 -0
  167. data/spec/grape/api_remount_spec.rb +473 -0
  168. data/spec/grape/api_spec.rb +565 -12
  169. data/spec/grape/config_spec.rb +19 -0
  170. data/spec/grape/dsl/callbacks_spec.rb +2 -0
  171. data/spec/grape/dsl/configuration_spec.rb +2 -0
  172. data/spec/grape/dsl/desc_spec.rb +42 -16
  173. data/spec/grape/dsl/headers_spec.rb +2 -0
  174. data/spec/grape/dsl/helpers_spec.rb +4 -2
  175. data/spec/grape/dsl/inside_route_spec.rb +184 -33
  176. data/spec/grape/dsl/logger_spec.rb +2 -0
  177. data/spec/grape/dsl/middleware_spec.rb +10 -0
  178. data/spec/grape/dsl/parameters_spec.rb +2 -0
  179. data/spec/grape/dsl/request_response_spec.rb +2 -0
  180. data/spec/grape/dsl/routing_spec.rb +12 -0
  181. data/spec/grape/dsl/settings_spec.rb +2 -0
  182. data/spec/grape/dsl/validations_spec.rb +2 -0
  183. data/spec/grape/endpoint/declared_spec.rb +601 -0
  184. data/spec/grape/endpoint_spec.rb +53 -523
  185. data/spec/grape/entity_spec.rb +9 -1
  186. data/spec/grape/exceptions/base_spec.rb +67 -0
  187. data/spec/grape/exceptions/body_parse_errors_spec.rb +2 -0
  188. data/spec/grape/exceptions/invalid_accept_header_spec.rb +2 -0
  189. data/spec/grape/exceptions/invalid_formatter_spec.rb +2 -0
  190. data/spec/grape/exceptions/invalid_response_spec.rb +13 -0
  191. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -0
  192. data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
  193. data/spec/grape/exceptions/missing_option_spec.rb +2 -0
  194. data/spec/grape/exceptions/unknown_options_spec.rb +2 -0
  195. data/spec/grape/exceptions/unknown_validator_spec.rb +2 -0
  196. data/spec/grape/exceptions/validation_errors_spec.rb +8 -4
  197. data/spec/grape/exceptions/validation_spec.rb +3 -1
  198. data/spec/grape/extensions/param_builders/hash_spec.rb +2 -0
  199. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +2 -0
  200. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +2 -0
  201. data/spec/grape/integration/global_namespace_function_spec.rb +2 -0
  202. data/spec/grape/integration/rack_sendfile_spec.rb +14 -8
  203. data/spec/grape/integration/rack_spec.rb +25 -7
  204. data/spec/grape/loading_spec.rb +2 -0
  205. data/spec/grape/middleware/auth/base_spec.rb +2 -0
  206. data/spec/grape/middleware/auth/dsl_spec.rb +5 -3
  207. data/spec/grape/middleware/auth/strategies_spec.rb +3 -1
  208. data/spec/grape/middleware/base_spec.rb +10 -0
  209. data/spec/grape/middleware/error_spec.rb +3 -1
  210. data/spec/grape/middleware/exception_spec.rb +4 -2
  211. data/spec/grape/middleware/formatter_spec.rb +33 -16
  212. data/spec/grape/middleware/globals_spec.rb +2 -0
  213. data/spec/grape/middleware/stack_spec.rb +12 -0
  214. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
  215. data/spec/grape/middleware/versioner/header_spec.rb +9 -1
  216. data/spec/grape/middleware/versioner/param_spec.rb +3 -1
  217. data/spec/grape/middleware/versioner/path_spec.rb +3 -1
  218. data/spec/grape/middleware/versioner_spec.rb +2 -0
  219. data/spec/grape/named_api_spec.rb +21 -0
  220. data/spec/grape/parser_spec.rb +7 -5
  221. data/spec/grape/path_spec.rb +6 -4
  222. data/spec/grape/presenters/presenter_spec.rb +2 -0
  223. data/spec/grape/request_spec.rb +26 -0
  224. data/spec/grape/util/inheritable_setting_spec.rb +2 -0
  225. data/spec/grape/util/inheritable_values_spec.rb +2 -0
  226. data/spec/grape/util/reverse_stackable_values_spec.rb +2 -0
  227. data/spec/grape/util/stackable_values_spec.rb +3 -1
  228. data/spec/grape/util/strict_hash_configuration_spec.rb +2 -0
  229. data/spec/grape/validations/attributes_iterator_spec.rb +2 -0
  230. data/spec/grape/validations/instance_behaivour_spec.rb +5 -3
  231. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +41 -0
  232. data/spec/grape/validations/params_scope_spec.rb +213 -9
  233. data/spec/grape/validations/single_attribute_iterator_spec.rb +58 -0
  234. data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
  235. data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
  236. data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
  237. data/spec/grape/validations/types_spec.rb +9 -36
  238. data/spec/grape/validations/validators/all_or_none_spec.rb +140 -30
  239. data/spec/grape/validations/validators/allow_blank_spec.rb +2 -0
  240. data/spec/grape/validations/validators/at_least_one_of_spec.rb +175 -29
  241. data/spec/grape/validations/validators/coerce_spec.rb +476 -135
  242. data/spec/grape/validations/validators/default_spec.rb +172 -0
  243. data/spec/grape/validations/validators/exactly_one_of_spec.rb +204 -38
  244. data/spec/grape/validations/validators/except_values_spec.rb +4 -1
  245. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +186 -27
  246. data/spec/grape/validations/validators/presence_spec.rb +30 -0
  247. data/spec/grape/validations/validators/regexp_spec.rb +2 -0
  248. data/spec/grape/validations/validators/same_as_spec.rb +65 -0
  249. data/spec/grape/validations/validators/values_spec.rb +30 -5
  250. data/spec/grape/validations_spec.rb +388 -50
  251. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  252. data/spec/integration/multi_json/json_spec.rb +2 -0
  253. data/spec/integration/multi_xml/xml_spec.rb +2 -0
  254. data/spec/shared/versioning_examples.rb +22 -20
  255. data/spec/spec_helper.rb +12 -1
  256. data/spec/support/basic_auth_encode_helpers.rb +2 -0
  257. data/spec/support/chunks.rb +14 -0
  258. data/spec/support/content_type_helpers.rb +2 -0
  259. data/spec/support/eager_load.rb +19 -0
  260. data/spec/support/endpoint_faker.rb +2 -0
  261. data/spec/support/file_streamer.rb +2 -0
  262. data/spec/support/integer_helpers.rb +2 -0
  263. data/spec/support/versioned_helpers.rb +8 -8
  264. metadata +86 -48
  265. data/Appraisals +0 -32
  266. data/Dangerfile +0 -2
  267. data/Gemfile +0 -33
  268. data/Gemfile.lock +0 -231
  269. data/Guardfile +0 -10
  270. data/RELEASING.md +0 -111
  271. data/Rakefile +0 -25
  272. data/benchmark/simple.rb +0 -27
  273. data/benchmark/simple_with_type_coercer.rb +0 -22
  274. data/gemfiles/multi_json.gemfile +0 -35
  275. data/gemfiles/multi_xml.gemfile +0 -35
  276. data/gemfiles/rack_1.5.2.gemfile +0 -35
  277. data/gemfiles/rack_edge.gemfile +0 -35
  278. data/gemfiles/rails_3.gemfile +0 -36
  279. data/gemfiles/rails_4.gemfile +0 -35
  280. data/gemfiles/rails_5.gemfile +0 -35
  281. data/gemfiles/rails_edge.gemfile +0 -35
  282. data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
  283. data/lib/grape/util/content_types.rb +0 -26
  284. data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
  285. data/pkg/grape-0.17.0.gem +0 -0
  286. data/pkg/grape-0.19.0.gem +0 -0
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Validations do
@@ -7,16 +9,23 @@ describe Grape::Validations do
7
9
  subject
8
10
  end
9
11
 
12
+ def declared_params
13
+ subject.namespace_stackable(:declared_params).flatten
14
+ end
15
+
10
16
  describe 'params' do
11
17
  context 'optional' do
12
- it 'validates when params is present' do
18
+ before do
13
19
  subject.params do
14
20
  optional :a_number, regexp: /^[0-9]+$/
21
+ optional :attachment, type: File
15
22
  end
16
23
  subject.get '/optional' do
17
24
  'optional works!'
18
25
  end
26
+ end
19
27
 
28
+ it 'validates when params is present' do
20
29
  get '/optional', a_number: 'string'
21
30
  expect(last_response.status).to eq(400)
22
31
  expect(last_response.body).to eq('a_number is invalid')
@@ -27,14 +36,7 @@ describe Grape::Validations do
27
36
  end
28
37
 
29
38
  it "doesn't validate when param not present" do
30
- subject.params do
31
- optional :a_number, regexp: /^[0-9]+$/
32
- end
33
- subject.get '/optional' do
34
- 'optional works!'
35
- end
36
-
37
- get '/optional'
39
+ get '/optional', a_number: nil, attachment: nil
38
40
  expect(last_response.status).to eq(200)
39
41
  expect(last_response.body).to eq('optional works!')
40
42
  end
@@ -43,7 +45,7 @@ describe Grape::Validations do
43
45
  subject.params do
44
46
  optional :some_param
45
47
  end
46
- expect(subject.route_setting(:declared_params)).to eq([:some_param])
48
+ expect(declared_params).to eq([:some_param])
47
49
  end
48
50
  end
49
51
 
@@ -63,7 +65,7 @@ describe Grape::Validations do
63
65
 
64
66
  it 'adds entity documentation to declared params' do
65
67
  define_optional_using
66
- expect(subject.route_setting(:declared_params)).to eq(%i[field_a field_b])
68
+ expect(declared_params).to eq(%i[field_a field_b])
67
69
  end
68
70
 
69
71
  it 'works when field_a and field_b are not present' do
@@ -110,7 +112,7 @@ describe Grape::Validations do
110
112
  subject.params do
111
113
  requires :some_param
112
114
  end
113
- expect(subject.route_setting(:declared_params)).to eq([:some_param])
115
+ expect(declared_params).to eq([:some_param])
114
116
  end
115
117
 
116
118
  it 'works when required field is present but nil' do
@@ -120,6 +122,62 @@ describe Grape::Validations do
120
122
  end
121
123
  end
122
124
 
125
+ context 'requires with nested params' do
126
+ before do
127
+ subject.params do
128
+ requires :first_level, type: Hash do
129
+ optional :second_level, type: Array do
130
+ requires :value, type: Integer
131
+ optional :name, type: String
132
+ optional :third_level, type: Array do
133
+ requires :value, type: Integer
134
+ optional :name, type: String
135
+ optional :fourth_level, type: Array do
136
+ requires :value, type: Integer
137
+ optional :name, type: String
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ subject.put('/required') { 'required works' }
144
+ end
145
+
146
+ let(:request_params) do
147
+ {
148
+ first_level: {
149
+ second_level: [
150
+ { value: 1, name: 'Lisa' },
151
+ {
152
+ value: 2,
153
+ name: 'James',
154
+ third_level: [
155
+ { value: 'three', name: 'Sophie' },
156
+ {
157
+ value: 4,
158
+ name: 'Jenny',
159
+ fourth_level: [
160
+ { name: 'Samuel' }, { value: 6, name: 'Jane' }
161
+ ]
162
+ }
163
+ ]
164
+ }
165
+ ]
166
+ }
167
+ }
168
+ end
169
+
170
+ it 'validates correctly in deep nested params' do
171
+ put '/required', request_params.to_json, 'CONTENT_TYPE' => 'application/json'
172
+
173
+ expect(last_response.status).to eq(400)
174
+ expect(last_response.body).to eq(
175
+ 'first_level[second_level][1][third_level][0][value] is invalid, ' \
176
+ 'first_level[second_level][1][third_level][1][fourth_level][0][value] is missing'
177
+ )
178
+ end
179
+ end
180
+
123
181
  context 'requires :all using Grape::Entity documentation' do
124
182
  def define_requires_all
125
183
  documentation = {
@@ -139,7 +197,7 @@ describe Grape::Validations do
139
197
 
140
198
  it 'adds entity documentation to declared params' do
141
199
  define_requires_all
142
- expect(subject.route_setting(:declared_params)).to eq(%i[required_field optional_field])
200
+ expect(declared_params).to eq(%i[required_field optional_field])
143
201
  end
144
202
 
145
203
  it 'errors when required_field is not present' do
@@ -174,7 +232,7 @@ describe Grape::Validations do
174
232
 
175
233
  it 'adds entity documentation to declared params' do
176
234
  define_requires_none
177
- expect(subject.route_setting(:declared_params)).to eq(%i[required_field optional_field])
235
+ expect(declared_params).to eq(%i[required_field optional_field])
178
236
  end
179
237
 
180
238
  it 'errors when required_field is not present' do
@@ -204,7 +262,7 @@ describe Grape::Validations do
204
262
 
205
263
  it 'adds only the entity documentation to declared params, nothing more' do
206
264
  define_requires_all
207
- expect(subject.route_setting(:declared_params)).to eq(%i[required_field optional_field])
265
+ expect(declared_params).to eq(%i[required_field optional_field])
208
266
  end
209
267
  end
210
268
 
@@ -270,7 +328,7 @@ describe Grape::Validations do
270
328
  requires :key
271
329
  end
272
330
  end
273
- expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
331
+ expect(declared_params).to eq([items: [:key]])
274
332
  end
275
333
  end
276
334
 
@@ -342,7 +400,7 @@ describe Grape::Validations do
342
400
  requires :key
343
401
  end
344
402
  end
345
- expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
403
+ expect(declared_params).to eq([items: [:key]])
346
404
  end
347
405
  end
348
406
 
@@ -405,7 +463,7 @@ describe Grape::Validations do
405
463
  requires :key
406
464
  end
407
465
  end
408
- expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
466
+ expect(declared_params).to eq([items: [:key]])
409
467
  end
410
468
  end
411
469
 
@@ -436,7 +494,7 @@ describe Grape::Validations do
436
494
  class DateRangeValidator < Grape::Validations::Base
437
495
  def validate_param!(attr_name, params)
438
496
  return if params[attr_name][:from] <= params[attr_name][:to]
439
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: "'from' must be lower or equal to 'to'"
497
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: "'from' must be lower or equal to 'to'")
440
498
  end
441
499
  end
442
500
  end
@@ -520,7 +578,7 @@ describe Grape::Validations do
520
578
  # NOTE: with body parameters in json or XML or similar this
521
579
  # should actually fail with: children[parents][name] is missing.
522
580
  expect(last_response.status).to eq(400)
523
- expect(last_response.body).to eq('children[1][parents] is missing')
581
+ expect(last_response.body).to eq('children[1][parents] is missing, children[0][parents][1][name] is missing, children[0][parents][1][name] is empty')
524
582
  end
525
583
 
526
584
  it 'errors when a parameter is not present in array within array' do
@@ -540,7 +598,10 @@ describe Grape::Validations do
540
598
  ]
541
599
 
542
600
  expect(last_response.status).to eq(400)
543
- expect(last_response.body).to eq('children[0][parents] is missing, children[1][parents] is missing')
601
+ expect(last_response.body).to eq(
602
+ 'children[0][parents][0][name] is missing, ' \
603
+ 'children[1][parents][0][name] is missing'
604
+ )
544
605
  end
545
606
 
546
607
  it 'safely handles empty arrays and blank parameters' do
@@ -548,10 +609,17 @@ describe Grape::Validations do
548
609
  # should actually return 200, since an empty array is valid.
549
610
  get '/within_array', children: []
550
611
  expect(last_response.status).to eq(400)
551
- expect(last_response.body).to eq('children is missing')
612
+ expect(last_response.body).to eq(
613
+ 'children[0][name] is missing, ' \
614
+ 'children[0][parents] is missing, ' \
615
+ 'children[0][parents] is invalid, ' \
616
+ 'children[0][parents][0][name] is missing, ' \
617
+ 'children[0][parents][0][name] is empty'
618
+ )
619
+
552
620
  get '/within_array', children: [name: 'Jay']
553
621
  expect(last_response.status).to eq(400)
554
- expect(last_response.body).to eq('children[0][parents] is missing')
622
+ expect(last_response.body).to eq('children[0][parents] is missing, children[0][parents][0][name] is missing, children[0][parents][0][name] is empty')
555
623
  end
556
624
 
557
625
  it 'errors when param is not an Array' do
@@ -699,7 +767,7 @@ describe Grape::Validations do
699
767
  expect(last_response.status).to eq(200)
700
768
  put_with_json '/within_array', children: [name: 'Jay']
701
769
  expect(last_response.status).to eq(400)
702
- expect(last_response.body).to eq('children[0][parents] is missing')
770
+ expect(last_response.body).to eq('children[0][parents] is missing, children[0][parents][0][name] is missing')
703
771
  end
704
772
  end
705
773
 
@@ -749,7 +817,7 @@ describe Grape::Validations do
749
817
  requires :key
750
818
  end
751
819
  end
752
- expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
820
+ expect(declared_params).to eq([items: [:key]])
753
821
  end
754
822
  end
755
823
 
@@ -774,7 +842,7 @@ describe Grape::Validations do
774
842
  it 'does internal validations if the outer group is present' do
775
843
  get '/nested_optional_group', items: [{ key: 'foo' }]
776
844
  expect(last_response.status).to eq(400)
777
- expect(last_response.body).to eq('items[0][required_subitems] is missing')
845
+ expect(last_response.body).to eq('items[0][required_subitems] is missing, items[0][required_subitems][0][value] is missing')
778
846
 
779
847
  get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }] }]
780
848
  expect(last_response.status).to eq(200)
@@ -794,7 +862,7 @@ describe Grape::Validations do
794
862
  it 'handles validation within arrays' do
795
863
  get '/nested_optional_group', items: [{ key: 'foo' }]
796
864
  expect(last_response.status).to eq(400)
797
- expect(last_response.body).to eq('items[0][required_subitems] is missing')
865
+ expect(last_response.body).to eq('items[0][required_subitems] is missing, items[0][required_subitems][0][value] is missing')
798
866
 
799
867
  get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }] }]
800
868
  expect(last_response.status).to eq(200)
@@ -813,7 +881,275 @@ describe Grape::Validations do
813
881
  requires(:required_subitems, type: Array) { requires :value }
814
882
  end
815
883
  end
816
- expect(subject.route_setting(:declared_params)).to eq([items: [:key, { optional_subitems: [:value] }, { required_subitems: [:value] }]])
884
+ expect(declared_params).to eq([items: [:key, { optional_subitems: [:value] }, { required_subitems: [:value] }]])
885
+ end
886
+
887
+ context <<~DESC do
888
+ Issue occurs whenever:
889
+ * param structure with at least three levels
890
+ * 1st level item is a required Array that has >1 entry with an optional item present and >1 entry with an optional item missing
891
+ * 2nd level is an optional Array or Hash
892
+ * 3rd level is a required item (can be any type)
893
+ * additional levels do not effect the issue from occuring
894
+ DESC
895
+
896
+ it "example based off actual real world use case" do
897
+ subject.params do
898
+ requires :orders, type: Array do
899
+ requires :id, type: Integer
900
+ optional :drugs, type: Array do
901
+ requires :batches, type: Array do
902
+ requires :batch_no, type: String
903
+ end
904
+ end
905
+ end
906
+ end
907
+
908
+ subject.get '/validate_required_arrays_under_optional_arrays' do
909
+ 'validate_required_arrays_under_optional_arrays works!'
910
+ end
911
+
912
+ data = {
913
+ orders: [
914
+ { id: 77, drugs: [{batches: [{batch_no: "A1234567"}]}]},
915
+ { id: 70 }
916
+ ]
917
+ }
918
+
919
+ get '/validate_required_arrays_under_optional_arrays', data
920
+ expect(last_response.body).to eq("validate_required_arrays_under_optional_arrays works!")
921
+ expect(last_response.status).to eq(200)
922
+ end
923
+
924
+ it "simplest example using Array -> Array -> Hash -> String" do
925
+ subject.params do
926
+ requires :orders, type: Array do
927
+ requires :id, type: Integer
928
+ optional :drugs, type: Array do
929
+ requires :batch_no, type: String
930
+ end
931
+ end
932
+ end
933
+
934
+ subject.get '/validate_required_arrays_under_optional_arrays' do
935
+ 'validate_required_arrays_under_optional_arrays works!'
936
+ end
937
+
938
+ data = {
939
+ orders: [
940
+ { id: 77, drugs: [{batch_no: "A1234567"}]},
941
+ { id: 70 }
942
+ ]
943
+ }
944
+
945
+ get '/validate_required_arrays_under_optional_arrays', data
946
+ expect(last_response.body).to eq("validate_required_arrays_under_optional_arrays works!")
947
+ expect(last_response.status).to eq(200)
948
+ end
949
+
950
+ it "simplest example using Array -> Hash -> String" do
951
+ subject.params do
952
+ requires :orders, type: Array do
953
+ requires :id, type: Integer
954
+ optional :drugs, type: Hash do
955
+ requires :batch_no, type: String
956
+ end
957
+ end
958
+ end
959
+
960
+ subject.get '/validate_required_arrays_under_optional_arrays' do
961
+ 'validate_required_arrays_under_optional_arrays works!'
962
+ end
963
+
964
+ data = {
965
+ orders: [
966
+ { id: 77, drugs: {batch_no: "A1234567"}},
967
+ { id: 70 }
968
+ ]
969
+ }
970
+
971
+ get '/validate_required_arrays_under_optional_arrays', data
972
+ expect(last_response.body).to eq("validate_required_arrays_under_optional_arrays works!")
973
+ expect(last_response.status).to eq(200)
974
+ end
975
+
976
+ it "correctly indexes invalida data" do
977
+ subject.params do
978
+ requires :orders, type: Array do
979
+ requires :id, type: Integer
980
+ optional :drugs, type: Array do
981
+ requires :batch_no, type: String
982
+ requires :quantity, type: Integer
983
+ end
984
+ end
985
+ end
986
+
987
+ subject.get '/correctly_indexes' do
988
+ 'correctly_indexes works!'
989
+ end
990
+
991
+ data = {
992
+ orders: [
993
+ { id: 70 },
994
+ { id: 77, drugs: [{batch_no: "A1234567", quantity: 12}, {batch_no: "B222222"}]}
995
+ ]
996
+ }
997
+
998
+ get '/correctly_indexes', data
999
+ expect(last_response.body).to eq("orders[1][drugs][1][quantity] is missing")
1000
+ expect(last_response.status).to eq(400)
1001
+ end
1002
+
1003
+ context "multiple levels of optional and requires settings" do
1004
+ before do
1005
+ subject.params do
1006
+ requires :top, type: Array do
1007
+ requires :top_id, type: Integer, allow_blank: false
1008
+ optional :middle_1, type: Array do
1009
+ requires :middle_1_id, type: Integer, allow_blank: false
1010
+ optional :middle_2, type: Array do
1011
+ requires :middle_2_id, type: String, allow_blank: false
1012
+ optional :bottom, type: Array do
1013
+ requires :bottom_id, type: Integer, allow_blank: false
1014
+ end
1015
+ end
1016
+ end
1017
+ end
1018
+ end
1019
+
1020
+ subject.get '/multi_level' do
1021
+ 'multi_level works!'
1022
+ end
1023
+ end
1024
+
1025
+ it "with valid data" do
1026
+ data = {
1027
+ top: [
1028
+ { top_id: 1, middle_1: [
1029
+ {middle_1_id: 11}, {middle_1_id: 12, middle_2: [
1030
+ {middle_2_id: 121}, {middle_2_id: 122, bottom: [{bottom_id: 1221}]}]}]},
1031
+ { top_id: 2, middle_1: [
1032
+ {middle_1_id: 21}, {middle_1_id: 22, middle_2: [
1033
+ {middle_2_id: 221}]}]},
1034
+ { top_id: 3, middle_1: [
1035
+ {middle_1_id: 31}, {middle_1_id: 32}]},
1036
+ { top_id: 4 }
1037
+ ]
1038
+ }
1039
+
1040
+ get '/multi_level', data
1041
+ expect(last_response.body).to eq("multi_level works!")
1042
+ expect(last_response.status).to eq(200)
1043
+ end
1044
+
1045
+ it "with invalid data" do
1046
+ data = {
1047
+ top: [
1048
+ { top_id: 1, middle_1: [
1049
+ {middle_1_id: 11}, {middle_1_id: 12, middle_2: [
1050
+ {middle_2_id: 121}, {middle_2_id: 122, bottom: [{bottom_id: nil}]}]}]},
1051
+ { top_id: 2, middle_1: [
1052
+ {middle_1_id: 21}, {middle_1_id: 22, middle_2: [{middle_2_id: nil}]}]},
1053
+ { top_id: 3, middle_1: [
1054
+ {middle_1_id: nil}, {middle_1_id: 32}]},
1055
+ { top_id: nil, missing_top_id: 4 }
1056
+ ]
1057
+ }
1058
+ # debugger
1059
+ get '/multi_level', data
1060
+ expect(last_response.body.split(", ")).to match_array([
1061
+ "top[3][top_id] is empty",
1062
+ "top[2][middle_1][0][middle_1_id] is empty",
1063
+ "top[1][middle_1][1][middle_2][0][middle_2_id] is empty",
1064
+ "top[0][middle_1][1][middle_2][1][bottom][0][bottom_id] is empty"
1065
+ ])
1066
+ expect(last_response.status).to eq(400)
1067
+ end
1068
+ end
1069
+ end
1070
+
1071
+ it "exactly_one_of" do
1072
+ subject.params do
1073
+ requires :orders, type: Array do
1074
+ requires :id, type: Integer
1075
+ optional :drugs, type: Hash do
1076
+ optional :batch_no, type: String
1077
+ optional :batch_id, type: String
1078
+ exactly_one_of :batch_no, :batch_id
1079
+ end
1080
+ end
1081
+ end
1082
+
1083
+ subject.get '/exactly_one_of' do
1084
+ 'exactly_one_of works!'
1085
+ end
1086
+
1087
+ data = {
1088
+ orders: [
1089
+ { id: 77, drugs: {batch_no: "A1234567"}},
1090
+ { id: 70 }
1091
+ ]
1092
+ }
1093
+
1094
+ get '/exactly_one_of', data
1095
+ expect(last_response.body).to eq("exactly_one_of works!")
1096
+ expect(last_response.status).to eq(200)
1097
+ end
1098
+
1099
+ it "at_least_one_of" do
1100
+ subject.params do
1101
+ requires :orders, type: Array do
1102
+ requires :id, type: Integer
1103
+ optional :drugs, type: Hash do
1104
+ optional :batch_no, type: String
1105
+ optional :batch_id, type: String
1106
+ at_least_one_of :batch_no, :batch_id
1107
+ end
1108
+ end
1109
+ end
1110
+
1111
+ subject.get '/at_least_one_of' do
1112
+ 'at_least_one_of works!'
1113
+ end
1114
+
1115
+ data = {
1116
+ orders: [
1117
+ { id: 77, drugs: {batch_no: "A1234567"}},
1118
+ { id: 70 }
1119
+ ]
1120
+ }
1121
+
1122
+ get '/at_least_one_of', data
1123
+ expect(last_response.body).to eq("at_least_one_of works!")
1124
+ expect(last_response.status).to eq(200)
1125
+ end
1126
+
1127
+ it "all_or_none_of" do
1128
+ subject.params do
1129
+ requires :orders, type: Array do
1130
+ requires :id, type: Integer
1131
+ optional :drugs, type: Hash do
1132
+ optional :batch_no, type: String
1133
+ optional :batch_id, type: String
1134
+ all_or_none_of :batch_no, :batch_id
1135
+ end
1136
+ end
1137
+ end
1138
+
1139
+ subject.get '/all_or_none_of' do
1140
+ 'all_or_none_of works!'
1141
+ end
1142
+
1143
+ data = {
1144
+ orders: [
1145
+ { id: 77, drugs: {batch_no: "A1234567", batch_id: "12"}},
1146
+ { id: 70 }
1147
+ ]
1148
+ }
1149
+
1150
+ get '/all_or_none_of', data
1151
+ expect(last_response.body).to eq("all_or_none_of works!")
1152
+ expect(last_response.status).to eq(200)
817
1153
  end
818
1154
  end
819
1155
 
@@ -841,7 +1177,7 @@ describe Grape::Validations do
841
1177
  class Customvalidator < Grape::Validations::Base
842
1178
  def validate_param!(attr_name, params)
843
1179
  return if params[attr_name] == 'im custom'
844
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: 'is not custom!'
1180
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: 'is not custom!')
845
1181
  end
846
1182
  end
847
1183
  end
@@ -989,7 +1325,7 @@ describe Grape::Validations do
989
1325
  class CustomvalidatorWithOptions < Grape::Validations::Base
990
1326
  def validate_param!(attr_name, params)
991
1327
  return if params[attr_name] == @option[:text]
992
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message
1328
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message)
993
1329
  end
994
1330
  end
995
1331
  end
@@ -1013,7 +1349,7 @@ describe Grape::Validations do
1013
1349
  expect(last_response.body).to eq('custom is not custom with options!')
1014
1350
  end
1015
1351
  end
1016
- end # end custom validation
1352
+ end
1017
1353
 
1018
1354
  context 'named' do
1019
1355
  context 'can be defined' do
@@ -1058,14 +1394,14 @@ describe Grape::Validations do
1058
1394
  subject.params do
1059
1395
  use :pagination
1060
1396
  end
1061
- expect(subject.route_setting(:declared_params)).to eq %i[page per_page]
1397
+ expect(declared_params).to eq %i[page per_page]
1062
1398
  end
1063
1399
 
1064
1400
  it 'by #use with multiple params' do
1065
1401
  subject.params do
1066
1402
  use :pagination, :period
1067
1403
  end
1068
- expect(subject.route_setting(:declared_params)).to eq %i[page per_page start_date end_date]
1404
+ expect(declared_params).to eq %i[page per_page start_date end_date]
1069
1405
  end
1070
1406
  end
1071
1407
 
@@ -1223,7 +1559,9 @@ describe Grape::Validations do
1223
1559
  end
1224
1560
  get '/custom_message/mutually_exclusive', beer: 'true', wine: 'true', nested: { scotch: 'true', aquavit: 'true' }, nested2: [{ scotch2: 'true' }, { scotch2: 'true', aquavit2: 'true' }]
1225
1561
  expect(last_response.status).to eq(400)
1226
- expect(last_response.body).to eq 'beer, wine are mutually exclusive pass only one, scotch, aquavit are mutually exclusive pass only one, scotch2, aquavit2 are mutually exclusive pass only one'
1562
+ expect(last_response.body).to eq(
1563
+ 'beer, wine are mutually exclusive pass only one, nested[scotch], nested[aquavit] are mutually exclusive pass only one, nested2[1][scotch2], nested2[1][aquavit2] are mutually exclusive pass only one'
1564
+ )
1227
1565
  end
1228
1566
  end
1229
1567
 
@@ -1249,7 +1587,7 @@ describe Grape::Validations do
1249
1587
 
1250
1588
  get '/mutually_exclusive', beer: 'true', wine: 'true', nested: { scotch: 'true', aquavit: 'true' }, nested2: [{ scotch2: 'true' }, { scotch2: 'true', aquavit2: 'true' }]
1251
1589
  expect(last_response.status).to eq(400)
1252
- expect(last_response.body).to eq 'beer, wine are mutually exclusive, scotch, aquavit are mutually exclusive, scotch2, aquavit2 are mutually exclusive'
1590
+ expect(last_response.body).to eq 'beer, wine are mutually exclusive, nested[scotch], nested[aquavit] are mutually exclusive, nested2[1][scotch2], nested2[1][aquavit2] are mutually exclusive'
1253
1591
  end
1254
1592
  end
1255
1593
 
@@ -1318,7 +1656,7 @@ describe Grape::Validations do
1318
1656
  optional :beer
1319
1657
  optional :wine
1320
1658
  optional :juice
1321
- exactly_one_of :beer, :wine, :juice, message: { exactly_one: 'are missing, exactly one parameter is required', mutual_exclusion: 'are mutually exclusive, exactly one parameter is required' }
1659
+ exactly_one_of :beer, :wine, :juice, message: 'are missing, exactly one parameter is required'
1322
1660
  end
1323
1661
  get '/exactly_one_of' do
1324
1662
  'exactly_one_of works!'
@@ -1352,7 +1690,7 @@ describe Grape::Validations do
1352
1690
  it 'errors when two or more are present' do
1353
1691
  get '/custom_message/exactly_one_of', beer: 'string', wine: 'anotherstring'
1354
1692
  expect(last_response.status).to eq(400)
1355
- expect(last_response.body).to eq 'beer, wine are mutually exclusive, exactly one parameter is required'
1693
+ expect(last_response.body).to eq 'beer, wine are missing, exactly one parameter is required'
1356
1694
  end
1357
1695
  end
1358
1696
 
@@ -1399,7 +1737,7 @@ describe Grape::Validations do
1399
1737
  it 'errors when none are present' do
1400
1738
  get '/exactly_one_of_nested'
1401
1739
  expect(last_response.status).to eq(400)
1402
- expect(last_response.body).to eq 'nested is missing, beer_nested, wine_nested, juice_nested are missing, exactly one parameter must be provided'
1740
+ expect(last_response.body).to eq 'nested is missing, nested[beer_nested], nested[wine_nested], nested[juice_nested] are missing, exactly one parameter must be provided'
1403
1741
  end
1404
1742
 
1405
1743
  it 'succeeds when one is present' do
@@ -1411,7 +1749,7 @@ describe Grape::Validations do
1411
1749
  it 'errors when two or more are present' do
1412
1750
  get '/exactly_one_of_nested', nested: { beer_nested: 'string' }, nested2: [{ beer_nested2: 'string', wine_nested2: 'anotherstring' }]
1413
1751
  expect(last_response.status).to eq(400)
1414
- expect(last_response.body).to eq 'beer_nested2, wine_nested2 are mutually exclusive'
1752
+ expect(last_response.body).to eq 'nested2[0][beer_nested2], nested2[0][wine_nested2] are mutually exclusive'
1415
1753
  end
1416
1754
  end
1417
1755
  end
@@ -1485,16 +1823,16 @@ describe Grape::Validations do
1485
1823
  before :each do
1486
1824
  subject.params do
1487
1825
  requires :nested, type: Hash do
1488
- optional :beer_nested
1489
- optional :wine_nested
1490
- optional :juice_nested
1491
- at_least_one_of :beer_nested, :wine_nested, :juice_nested
1826
+ optional :beer
1827
+ optional :wine
1828
+ optional :juice
1829
+ at_least_one_of :beer, :wine, :juice
1492
1830
  end
1493
1831
  optional :nested2, type: Array do
1494
- optional :beer_nested2
1495
- optional :wine_nested2
1496
- optional :juice_nested2
1497
- at_least_one_of :beer_nested2, :wine_nested2, :juice_nested2
1832
+ optional :beer
1833
+ optional :wine
1834
+ optional :juice
1835
+ at_least_one_of :beer, :wine, :juice
1498
1836
  end
1499
1837
  end
1500
1838
  subject.get '/at_least_one_of_nested' do
@@ -1505,17 +1843,17 @@ describe Grape::Validations do
1505
1843
  it 'errors when none are present' do
1506
1844
  get '/at_least_one_of_nested'
1507
1845
  expect(last_response.status).to eq(400)
1508
- expect(last_response.body).to eq 'nested is missing, beer_nested, wine_nested, juice_nested are missing, at least one parameter must be provided'
1846
+ expect(last_response.body).to eq 'nested is missing, nested[beer], nested[wine], nested[juice] are missing, at least one parameter must be provided'
1509
1847
  end
1510
1848
 
1511
1849
  it 'does not error when one is present' do
1512
- get '/at_least_one_of_nested', nested: { beer_nested: 'string' }, nested2: [{ beer_nested2: 'string' }]
1850
+ get '/at_least_one_of_nested', nested: { beer: 'string' }, nested2: [{ beer: 'string' }]
1513
1851
  expect(last_response.status).to eq(200)
1514
1852
  expect(last_response.body).to eq 'at_least_one_of works!'
1515
1853
  end
1516
1854
 
1517
1855
  it 'does not error when two are present' do
1518
- get '/at_least_one_of_nested', nested: { beer_nested: 'string', wine_nested: 'string' }, nested2: [{ beer_nested2: 'string', wine_nested2: 'string' }]
1856
+ get '/at_least_one_of_nested', nested: { beer: 'string', wine: 'string' }, nested2: [{ beer: 'string', wine: 'string' }]
1519
1857
  expect(last_response.status).to eq(200)
1520
1858
  expect(last_response.body).to eq 'at_least_one_of works!'
1521
1859
  end