Skip to content

Commit f0ed9c5

Browse files
committed
Refactor mutual exclusion spec to use fake API
1 parent cd6d7fd commit f0ed9c5

File tree

1 file changed

+184
-27
lines changed

1 file changed

+184
-27
lines changed

spec/grape/validations/validators/mutual_exclusion_spec.rb

+184-27
Original file line numberDiff line numberDiff line change
@@ -2,61 +2,218 @@
22

33
describe Grape::Validations::MutualExclusionValidator do
44
describe '#validate!' do
5-
let(:scope) do
6-
Struct.new(:opts) do
7-
def params(arg)
8-
arg
5+
subject(:validate) { post path, params }
6+
7+
module ValidationsSpec
8+
module MutualExclusionValidatorSpec
9+
class API < Grape::API
10+
rescue_from Grape::Exceptions::ValidationErrors do |e|
11+
error!(e.errors.transform_keys! { |key| key.join(',') }, 400)
12+
end
13+
14+
params do
15+
optional :beer
16+
optional :wine
17+
optional :grapefruit
18+
mutually_exclusive :beer, :wine, :grapefruit
19+
end
20+
post do
21+
end
22+
23+
params do
24+
optional :beer
25+
optional :wine
26+
optional :grapefruit
27+
optional :other
28+
mutually_exclusive :beer, :wine, :grapefruit
29+
end
30+
post 'mixed-params' do
31+
end
32+
33+
params do
34+
optional :beer
35+
optional :wine
36+
optional :grapefruit
37+
mutually_exclusive :beer, :wine, :grapefruit, message: 'you should not mix beer and wine'
38+
end
39+
post '/custom-message' do
40+
end
41+
42+
params do
43+
requires :item, type: Hash do
44+
optional :beer
45+
optional :wine
46+
optional :grapefruit
47+
mutually_exclusive :beer, :wine, :grapefruit
48+
end
49+
end
50+
post '/nested-hash' do
51+
end
52+
53+
params do
54+
optional :item, type: Hash do
55+
optional :beer
56+
optional :wine
57+
optional :grapefruit
58+
mutually_exclusive :beer, :wine, :grapefruit
59+
end
60+
end
61+
post '/nested-optional-hash' do
62+
end
63+
64+
params do
65+
requires :items, type: Array do
66+
optional :beer
67+
optional :wine
68+
optional :grapefruit
69+
mutually_exclusive :beer, :wine, :grapefruit
70+
end
71+
end
72+
post '/nested-array' do
73+
end
74+
75+
params do
76+
requires :items, type: Array do
77+
requires :nested_items, type: Array do
78+
optional :beer, :wine, :grapefruit, type: Boolean
79+
mutually_exclusive :beer, :wine, :grapefruit
80+
end
81+
end
82+
end
83+
post '/deeply-nested-array' do
84+
end
985
end
1086
end
1187
end
12-
let(:mutually_exclusive_params) { %i[beer wine grapefruit] }
13-
let(:validator) { described_class.new(mutually_exclusive_params, {}, false, scope.new) }
88+
89+
def app
90+
ValidationsSpec::MutualExclusionValidatorSpec::API
91+
end
1492

1593
context 'when all mutually exclusive params are present' do
94+
let(:path) { '/' }
1695
let(:params) { { beer: true, wine: true, grapefruit: true } }
1796

18-
it 'raises a validation exception' do
19-
expect do
20-
validator.validate! params
21-
end.to raise_error(Grape::Exceptions::Validation)
97+
it 'returns a validation error' do
98+
validate
99+
expect(last_response.status).to eq 400
100+
expect(JSON.parse(last_response.body)).to eq(
101+
'beer,wine,grapefruit' => ['are mutually exclusive']
102+
)
22103
end
23104

24105
context 'mixed with other params' do
25-
let(:mixed_params) { params.merge!(other: true, andanother: true) }
106+
let(:path) { '/mixed-params' }
107+
let(:params) { { beer: true, wine: true, grapefruit: true, other: true } }
26108

27-
it 'still raises a validation exception' do
28-
expect do
29-
validator.validate! mixed_params
30-
end.to raise_error(Grape::Exceptions::Validation)
109+
it 'returns a validation error' do
110+
validate
111+
expect(last_response.status).to eq 400
112+
expect(JSON.parse(last_response.body)).to eq(
113+
'beer,wine,grapefruit' => ['are mutually exclusive']
114+
)
31115
end
32116
end
33117
end
34118

35119
context 'when a subset of mutually exclusive params are present' do
120+
let(:path) { '/' }
36121
let(:params) { { beer: true, grapefruit: true } }
37122

38-
it 'raises a validation exception' do
39-
expect do
40-
validator.validate! params
41-
end.to raise_error(Grape::Exceptions::Validation)
123+
it 'returns a validation error' do
124+
validate
125+
expect(last_response.status).to eq 400
126+
expect(JSON.parse(last_response.body)).to eq(
127+
'beer,grapefruit' => ['are mutually exclusive']
128+
)
42129
end
43130
end
44131

45-
context 'when params keys come as strings' do
46-
let(:params) { { 'beer' => true, 'grapefruit' => true } }
132+
context 'when custom message is specified' do
133+
let(:path) { '/custom-message' }
134+
let(:params) { { beer: true, wine: true } }
47135

48-
it 'raises a validation exception' do
49-
expect do
50-
validator.validate! params
51-
end.to raise_error(Grape::Exceptions::Validation)
136+
it 'returns a validation error' do
137+
validate
138+
expect(last_response.status).to eq 400
139+
expect(JSON.parse(last_response.body)).to eq(
140+
'beer,wine' => ['you should not mix beer and wine']
141+
)
52142
end
53143
end
54144

55145
context 'when no mutually exclusive params are present' do
146+
let(:path) { '/' }
56147
let(:params) { { beer: true, somethingelse: true } }
57148

58-
it 'params' do
59-
expect(validator.validate!(params)).to eql params
149+
it 'does not return a validation error' do
150+
validate
151+
expect(last_response.status).to eq 201
152+
end
153+
end
154+
155+
context 'when mutually exclusive params are nested inside required hash' do
156+
let(:path) { '/nested-hash' }
157+
let(:params) { { item: { beer: true, wine: true } } }
158+
159+
it 'returns a validation error with full names of the params' do
160+
validate
161+
expect(last_response.status).to eq 400
162+
expect(JSON.parse(last_response.body)).to eq(
163+
'item[beer],item[wine]' => ['are mutually exclusive']
164+
)
165+
end
166+
end
167+
168+
context 'when mutually exclusive params are nested inside optional hash' do
169+
let(:path) { '/nested-optional-hash' }
170+
171+
context 'when params are passed' do
172+
let(:params) { { item: { beer: true, wine: true } } }
173+
174+
it 'returns a validation error with full names of the params' do
175+
validate
176+
expect(last_response.status).to eq 400
177+
expect(JSON.parse(last_response.body)).to eq(
178+
'item[beer],item[wine]' => ['are mutually exclusive']
179+
)
180+
end
181+
end
182+
183+
context 'when params are empty' do
184+
let(:params) { {} }
185+
186+
it 'does not return a validation error' do
187+
validate
188+
expect(last_response.status).to eq 201
189+
end
190+
end
191+
end
192+
193+
context 'when mutually exclusive params are nested inside array' do
194+
let(:path) { '/nested-array' }
195+
let(:params) { { items: [{ beer: true, wine: true }, { wine: true, grapefruit: true }] } }
196+
197+
it 'returns a validation error with full names of the params' do
198+
validate
199+
expect(last_response.status).to eq 400
200+
expect(JSON.parse(last_response.body)).to eq(
201+
'items[0][beer],items[0][wine]' => ['are mutually exclusive'],
202+
'items[1][wine],items[1][grapefruit]' => ['are mutually exclusive']
203+
)
204+
end
205+
end
206+
207+
context 'when mutually exclusive params are deeply nested' do
208+
let(:path) { '/deeply-nested-array' }
209+
let(:params) { { items: [{ nested_items: [{ beer: true, wine: true }] }] } }
210+
211+
it 'returns a validation error with full names of the params' do
212+
validate
213+
expect(last_response.status).to eq 400
214+
expect(JSON.parse(last_response.body)).to eq(
215+
'items[0][nested_items][0][beer],items[0][nested_items][0][wine]' => ['are mutually exclusive']
216+
)
60217
end
61218
end
62219
end

0 commit comments

Comments
 (0)