|
2 | 2 |
|
3 | 3 | describe Grape::Validations::MutualExclusionValidator do
|
4 | 4 | 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 |
9 | 85 | end
|
10 | 86 | end
|
11 | 87 | 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 |
14 | 92 |
|
15 | 93 | context 'when all mutually exclusive params are present' do
|
| 94 | + let(:path) { '/' } |
16 | 95 | let(:params) { { beer: true, wine: true, grapefruit: true } }
|
17 | 96 |
|
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 | + ) |
22 | 103 | end
|
23 | 104 |
|
24 | 105 | 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 } } |
26 | 108 |
|
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 | + ) |
31 | 115 | end
|
32 | 116 | end
|
33 | 117 | end
|
34 | 118 |
|
35 | 119 | context 'when a subset of mutually exclusive params are present' do
|
| 120 | + let(:path) { '/' } |
36 | 121 | let(:params) { { beer: true, grapefruit: true } }
|
37 | 122 |
|
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 | + ) |
42 | 129 | end
|
43 | 130 | end
|
44 | 131 |
|
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 } } |
47 | 135 |
|
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 | + ) |
52 | 142 | end
|
53 | 143 | end
|
54 | 144 |
|
55 | 145 | context 'when no mutually exclusive params are present' do
|
| 146 | + let(:path) { '/' } |
56 | 147 | let(:params) { { beer: true, somethingelse: true } }
|
57 | 148 |
|
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 | + ) |
60 | 217 | end
|
61 | 218 | end
|
62 | 219 | end
|
|
0 commit comments