swaggable 0.6.0 → 0.7.0

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swaggable.rb +32 -7
  3. data/lib/swaggable/api_validator.rb +42 -0
  4. data/lib/swaggable/check_body_schema.rb +72 -0
  5. data/lib/swaggable/check_expected_parameters.rb +26 -0
  6. data/lib/swaggable/check_mandatory_parameters.rb +26 -0
  7. data/lib/swaggable/check_request_content_type.rb +26 -0
  8. data/lib/swaggable/check_response_code.rb +24 -0
  9. data/lib/swaggable/check_response_content_type.rb +26 -0
  10. data/lib/swaggable/endpoint_definition.rb +30 -2
  11. data/lib/swaggable/endpoint_validator.rb +27 -0
  12. data/lib/swaggable/errors.rb +9 -0
  13. data/lib/swaggable/errors/validations_collection.rb +44 -0
  14. data/lib/swaggable/grape_adapter.rb +2 -2
  15. data/lib/swaggable/mime_type_definition.rb +73 -0
  16. data/lib/swaggable/mime_types_collection.rb +60 -0
  17. data/lib/swaggable/parameter_definition.rb +19 -0
  18. data/lib/swaggable/query_params.rb +50 -0
  19. data/lib/swaggable/rack_request_adapter.rb +88 -0
  20. data/lib/swaggable/rack_response_adapter.rb +49 -0
  21. data/lib/swaggable/swagger_2_serializer.rb +3 -3
  22. data/lib/swaggable/validating_rack_app.rb +30 -0
  23. data/lib/swaggable/version.rb +1 -1
  24. data/spec/{swaggable/integration_spec.rb → integration/dsl_spec.rb} +0 -40
  25. data/spec/integration/rack_app_spec.rb +44 -0
  26. data/spec/integration/validating_rack_app_spec.rb +50 -0
  27. data/spec/spec_helper.rb +9 -5
  28. data/spec/swaggable/api_validator_spec.rb +61 -0
  29. data/spec/swaggable/check_body_schema_spec.rb +94 -0
  30. data/spec/swaggable/check_expected_parameters_spec.rb +110 -0
  31. data/spec/swaggable/check_mandatory_parameters_spec.rb +110 -0
  32. data/spec/swaggable/check_request_content_type_spec.rb +51 -0
  33. data/spec/swaggable/check_response_code_spec.rb +37 -0
  34. data/spec/swaggable/check_response_content_type_spec.rb +51 -0
  35. data/spec/swaggable/endpoint_definition_spec.rb +47 -0
  36. data/spec/swaggable/endpoint_validator_spec.rb +102 -0
  37. data/spec/swaggable/errors/validations_collection_spec.rb +78 -0
  38. data/spec/swaggable/grape_adapter_spec.rb +2 -2
  39. data/spec/swaggable/mime_type_definition_spec.rb +96 -0
  40. data/spec/swaggable/mime_types_collection_spec.rb +83 -0
  41. data/spec/swaggable/parameter_definition_spec.rb +23 -0
  42. data/spec/swaggable/query_params_spec.rb +37 -0
  43. data/spec/swaggable/rack_request_adapter_spec.rb +89 -0
  44. data/spec/swaggable/rack_response_adapter_spec.rb +46 -0
  45. data/spec/swaggable/swagger_2_serializer_spec.rb +4 -2
  46. data/spec/swaggable/swagger_2_validator_spec.rb +2 -2
  47. data/spec/swaggable/validating_rack_app_spec.rb +91 -0
  48. data/swaggable.gemspec +1 -0
  49. metadata +68 -4
@@ -0,0 +1,51 @@
1
+ require_relative '../spec_helper'
2
+
3
+ RSpec.describe 'Swaggable::CheckRequestContentType' do
4
+ subject { subject_instance }
5
+ let(:subject_instance) { subject_class.new endpoint: endpoint, request: request }
6
+ let(:subject_class) { Swaggable::CheckRequestContentType }
7
+
8
+ let(:content_types) { endpoint.consumes }
9
+ let(:endpoint) { Swaggable::EndpointDefinition.new }
10
+ let(:request) { Swaggable::RackRequestAdapter.new({}) }
11
+
12
+ describe '.call' do
13
+ def do_run
14
+ subject_class.call endpoint: endpoint, request: request
15
+ end
16
+
17
+ context 'no request content_type' do
18
+ it 'returns an empty errors collection' do
19
+ expect(do_run).to be_empty
20
+ end
21
+ end
22
+
23
+ context 'unsupported request content_type' do
24
+ before { request.content_type = 'application/json' }
25
+ before { content_types << :xml }
26
+
27
+ it 'returns errors collection' do
28
+ errors = do_run
29
+
30
+ expect(errors.count).to eq 1
31
+ expect(errors.first).to be_a Swaggable::Errors::Validation
32
+ end
33
+ end
34
+
35
+ context 'supported request content_type' do
36
+ before { content_types << :json }
37
+
38
+ it 'returns empty errors collection' do
39
+ errors = do_run
40
+ expect(errors.count).to eq 0
41
+ end
42
+
43
+ it 'still works with charset=utf-8' do
44
+ request.content_type = 'application/json; charset=utf-8'
45
+
46
+ errors = do_run
47
+ expect(errors.count).to eq 0
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,37 @@
1
+ require_relative '../spec_helper'
2
+
3
+ RSpec.describe 'Swaggable::CheckResponseCode' do
4
+ subject { subject_instance }
5
+ let(:subject_instance) { subject_class.new endpoint: endpoint, response: response }
6
+ let(:subject_class) { Swaggable::CheckResponseCode }
7
+
8
+ let(:endpoint) { Swaggable::EndpointDefinition.new }
9
+ let(:response) { Swaggable::RackResponseAdapter.new }
10
+
11
+ describe '.call' do
12
+ def do_run
13
+ subject_class.call endpoint: endpoint, response: response
14
+ end
15
+
16
+ context 'supported status code' do
17
+ it 'returns an empty errors collection' do
18
+ endpoint.responses.add_new{ status 200 }
19
+ response.code = 200
20
+
21
+ expect(do_run).to be_empty
22
+ end
23
+ end
24
+
25
+ context 'non supported status code' do
26
+ it 'returns an empty errors collection' do
27
+ endpoint.responses.add_new{ status 200 }
28
+ response.code = 418
29
+ errors = do_run
30
+
31
+ expect(errors.count).to eq 1
32
+ expect(errors.first).to be_a Swaggable::Errors::Validation
33
+ expect(errors.message).to match /code/
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,51 @@
1
+ require_relative '../spec_helper'
2
+
3
+ RSpec.describe 'Swaggable::CheckResponseContentType' do
4
+ subject { subject_instance }
5
+ let(:subject_instance) { subject_class.new endpoint: endpoint, response: response }
6
+ let(:subject_class) { Swaggable::CheckResponseContentType }
7
+
8
+ let(:content_types) { endpoint.produces }
9
+ let(:endpoint) { Swaggable::EndpointDefinition.new }
10
+ let(:response) { Swaggable::RackResponseAdapter.new }
11
+
12
+ describe '.call' do
13
+ def do_run
14
+ subject_class.call endpoint: endpoint, response: response
15
+ end
16
+
17
+ context 'no response content_type' do
18
+ it 'returns an empty errors collection' do
19
+ expect(do_run).to be_empty
20
+ end
21
+ end
22
+
23
+ context 'unsupported reponse content_type' do
24
+ before { response.content_type = 'application/json' }
25
+ before { content_types << :xml }
26
+
27
+ it 'returns errors collection' do
28
+ errors = do_run
29
+
30
+ expect(errors.count).to eq 1
31
+ expect(errors.first).to be_a Swaggable::Errors::Validation
32
+ end
33
+ end
34
+
35
+ context 'supported response content_type' do
36
+ before { content_types << :json }
37
+
38
+ it 'returns empty errors collection' do
39
+ errors = do_run
40
+ expect(errors.count).to eq 0
41
+ end
42
+
43
+ it 'still works with charset=utf-8' do
44
+ response.content_type = 'application/json; charset=utf-8'
45
+
46
+ errors = do_run
47
+ expect(errors.count).to eq 0
48
+ end
49
+ end
50
+ end
51
+ end
@@ -15,6 +15,15 @@ RSpec.describe 'Swaggable::EndpointDefinition' do
15
15
  expect(subject.verb).to eq 'POST'
16
16
  end
17
17
 
18
+ it 'verb is normalized' do
19
+ subject.verb = :post
20
+ expect(subject.verb).to eq 'POST'
21
+ end
22
+
23
+ it 'verb is GET by default' do
24
+ expect(subject_class.new.verb).to eq 'GET'
25
+ end
26
+
18
27
  it 'has a description' do
19
28
  subject.description = 'a new desc'
20
29
  expect(subject.description).to eq 'a new desc'
@@ -74,4 +83,42 @@ RSpec.describe 'Swaggable::EndpointDefinition' do
74
83
 
75
84
  expect(subject.responses[418].description).to eq 'Teapot'
76
85
  end
86
+
87
+ describe 'match?' do
88
+ it 'matches if verb and path are valid' do
89
+ endpoint = subject_class.new path: '/users/{id}/avatar', verb: 'GET'
90
+ expect(endpoint.match? :get, '/users/37/avatar').to be true
91
+ end
92
+
93
+ it 'doesn\'t match if verb is invalid' do
94
+ endpoint = subject_class.new path: '/users/{id}/avatar', verb: 'GET'
95
+ expect(endpoint.match? :put, '/users/37/avatar').to be false
96
+ end
97
+
98
+ it 'doesn\'t match if path is invalid' do
99
+ endpoint = subject_class.new path: '/users/{id}/quota', verb: 'GET'
100
+ expect(endpoint.match? :get, '/users/37/avatar').to be false
101
+ end
102
+ end
103
+
104
+ describe 'path_parameters_for' do
105
+ it 'returns path matches' do
106
+ endpoint = subject_class.new path: '/users/{id}/avatar', verb: 'GET'
107
+ match = endpoint.path_parameters_for '/users/37/avatar'
108
+ expect(match['id']).to eq '37'
109
+ end
110
+
111
+ it 'returns {} when no matches' do
112
+ endpoint = subject_class.new path: '/', verb: 'GET'
113
+ match = endpoint.path_parameters_for '/'
114
+ expect(match).to eq Hash.new
115
+ end
116
+ end
117
+
118
+ describe 'body' do
119
+ it 'returns the body param' do
120
+ body = subject.parameters.add_new { location :body }
121
+ expect(subject.body).to be body
122
+ end
123
+ end
77
124
  end
@@ -0,0 +1,102 @@
1
+ require_relative '../spec_helper'
2
+ require 'rack'
3
+
4
+ RSpec.describe 'Swaggable::EndpointValidator' do
5
+ subject { subject_instance }
6
+ let(:subject_instance) { subject_class.new endpoint: endpoint_definition, request: request }
7
+ let(:subject_class) { Swaggable::EndpointValidator }
8
+ let(:request) { request_for :get, '/existing_endpoint' }
9
+ let(:response) { Swaggable::RackResponseAdapter.new [200, {}, []] }
10
+ let(:endpoint_definition) { api_definition.endpoints.first }
11
+ let(:some_error) { double('some_error') }
12
+ let(:endpoint) { api_definition.endpoints.first }
13
+
14
+ let :api_definition do
15
+ Swaggable::ApiDefinition.new do
16
+ endpoints.add_new do
17
+ path '/existing_endpoint'
18
+ verb :get
19
+ end
20
+ end
21
+ end
22
+
23
+ def request_for verb, path
24
+ Swaggable::RackRequestAdapter.new Rack::MockRequest.env_for path, 'REQUEST_METHOD' => verb
25
+ end
26
+
27
+ describe '#errors_for_request(request)' do
28
+ def returns_some_error check
29
+ allow(check).to receive(:call).
30
+ with(endpoint: endpoint, request: request).
31
+ and_return([some_error])
32
+ end
33
+
34
+ def returns_no_error check
35
+ allow(check).to receive(:call).
36
+ with(endpoint: endpoint, request: request).
37
+ and_return([])
38
+ end
39
+
40
+ before do
41
+ returns_no_error Swaggable::CheckMandatoryParameters
42
+ returns_no_error Swaggable::CheckExpectedParameters
43
+ returns_no_error Swaggable::CheckRequestContentType
44
+ end
45
+
46
+ it 'is empty when all is correct' do
47
+ expect(subject.errors_for_request(request)).to be_empty
48
+ end
49
+
50
+ it 'validates the content type' do
51
+ returns_some_error Swaggable::CheckRequestContentType
52
+ expect(subject.errors_for_request(request)).to include(some_error)
53
+ end
54
+
55
+ it 'validates all mandatory parameters are present' do
56
+ returns_some_error Swaggable::CheckMandatoryParameters
57
+ expect(subject.errors_for_request(request)).to include(some_error)
58
+ end
59
+
60
+ it 'validates expected parameters' do
61
+ returns_some_error Swaggable::CheckExpectedParameters
62
+ expect(subject.errors_for_request(request)).to include(some_error)
63
+ end
64
+
65
+ it 'validates body schema' do
66
+ body = endpoint.parameters.add_new { location :body }
67
+ returns_some_error Swaggable::CheckBodySchema
68
+ expect(subject.errors_for_request(request)).to include(some_error)
69
+ end
70
+ end
71
+
72
+ describe '#errors_for_response(response)' do
73
+ def returns_some_error check
74
+ allow(check).to receive(:call).
75
+ with(endpoint: endpoint, response: response).
76
+ and_return([some_error])
77
+ end
78
+
79
+ def returns_no_error check
80
+ allow(check).to receive(:call).
81
+ with(endpoint: endpoint, response: response).
82
+ and_return([])
83
+ end
84
+
85
+ before do
86
+ returns_no_error Swaggable::CheckResponseContentType
87
+ end
88
+
89
+ it 'validates content type' do
90
+ returns_some_error Swaggable::CheckResponseContentType
91
+ expect(subject.errors_for_response(response)).to include(some_error)
92
+ end
93
+
94
+ it 'validates status code' do
95
+ returns_some_error Swaggable::CheckResponseCode
96
+ expect(subject.errors_for_response(response)).to include(some_error)
97
+ end
98
+
99
+ # TODO:
100
+ # it 'validates response body'
101
+ end
102
+ end
@@ -0,0 +1,78 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ RSpec.describe 'Swaggable::Errors::ValidationsCollection' do
4
+ let(:subject_class) { Swaggable::Errors::ValidationsCollection }
5
+ let(:subject_instance) { subject_class.new }
6
+ subject { subject_instance }
7
+ let(:error) { new_error }
8
+
9
+ def new_error *args
10
+ Swaggable::Errors::Validation.new *args
11
+ end
12
+
13
+ it 'can be instantiated' do
14
+ subject
15
+ end
16
+
17
+ describe '#<<' do
18
+ it 'adds Errors::Validation as is' do
19
+ subject << error
20
+ expect(subject.last).to be error
21
+ end
22
+ end
23
+
24
+ describe '#each' do
25
+ it 'iterates through the list' do
26
+ subject << error
27
+
28
+ list = []
29
+ subject.each {|e| list << e }
30
+
31
+ expect(list.last).to be error
32
+ end
33
+ end
34
+
35
+ describe '#inspect' do
36
+ it 'is readable' do
37
+ subject << new_error('Problem 1')
38
+ subject << new_error('Problem 2')
39
+ expect(subject.inspect).to eq "#<Swaggable::Errors::ValidationsCollection: Problem 1. Problem 2.>"
40
+ end
41
+ end
42
+
43
+ describe '#merge!' do
44
+ it 'joins arrays' do
45
+ other = subject_class.new
46
+
47
+ error_1 = new_error
48
+ error_2 = new_error
49
+
50
+ subject << new_error
51
+
52
+ subject.merge!([error_2])
53
+
54
+ expect(subject.to_a).to eq [error_1, error_2]
55
+ end
56
+ end
57
+
58
+ describe '#raise' do
59
+ it 'raises itself' do
60
+ expect{ raise subject }.to raise_error(subject_class)
61
+ end
62
+
63
+ it 'has a composite message' do
64
+ subject << new_error('Problem 1')
65
+ subject << new_error('Problem 2')
66
+
67
+ expect(subject.message).to eq 'Problem 1. Problem 2.'
68
+ end
69
+ end
70
+
71
+ describe '#empty' do
72
+ it 'is true when no errors are inside' do
73
+ expect(subject).to be_empty
74
+ subject << new_error('An error')
75
+ expect(subject).not_to be_empty
76
+ end
77
+ end
78
+ end
@@ -41,7 +41,7 @@ RSpec.describe 'Swaggable::GrapeAdapter' do
41
41
  it 'sets verb' do
42
42
  grape.post { }
43
43
  do_import
44
- expect(api.endpoints.first.verb).to eq 'post'
44
+ expect(api.endpoints.first.verb).to eq 'POST'
45
45
  end
46
46
 
47
47
  it 'sets description as summary' do
@@ -220,7 +220,7 @@ RSpec.describe 'Swaggable::GrapeAdapter' do
220
220
 
221
221
  do_import
222
222
 
223
- parameter = api.endpoints['POST /'].parameters[:first_name]
223
+ parameter = api.endpoints['POST /'].parameters['first_name']
224
224
  expect(parameter).to be parameter_definition
225
225
  end
226
226
  end
@@ -0,0 +1,96 @@
1
+ require_relative '../spec_helper'
2
+
3
+ RSpec.describe 'Swaggable::MimeTypeDefinition' do
4
+ let(:subject_class) { Swaggable::MimeTypeDefinition }
5
+ let(:subject_instance) { subject_class.new :json }
6
+ subject { subject_instance }
7
+
8
+ describe '.new' do
9
+ it 'accepts a symbol with the subtype' do
10
+ expect(subject.name).to eq('application/json')
11
+ end
12
+
13
+ it 'accepts a symbol with the subtype with underescores' do
14
+ subject = subject_class.new :octet_stream
15
+ expect(subject.name).to eq('application/octet-stream')
16
+ end
17
+
18
+ it 'accepts a name' do
19
+ expect(subject.name).to eq('application/json')
20
+ end
21
+
22
+ it 'accepts options' do
23
+ subject = subject_class.new 'application/json; charset=utf-8'
24
+ expect(subject.name).to eq('application/json')
25
+ end
26
+ end
27
+
28
+ describe '#name' do
29
+ it 'is type/subtype' do
30
+ expect(subject.name).to eq('application/json')
31
+ end
32
+ end
33
+
34
+ describe '#to_s' do
35
+ it 'is the name' do
36
+ expect(subject.to_s).to eq(subject.name)
37
+ end
38
+ end
39
+
40
+ describe '#http_string' do
41
+ it 'is "type/subtype; parameters"' do
42
+ expect(subject.http_string).to match(/^application\/json.*$/)
43
+ end
44
+
45
+ it 'contains options' do
46
+ subject = subject_class.new 'application/json; charset=utf-8'
47
+ expect(subject.http_string).to eq('application/json; charset=utf-8')
48
+ end
49
+ end
50
+
51
+ describe '#to_sym' do
52
+ describe 'when type is application' do
53
+ it 'is the subtype with underescores' do
54
+ subject = subject_class.new 'application/octet-stream'
55
+ expect(subject.to_sym).to eq(:octet_stream)
56
+ end
57
+ end
58
+
59
+ describe 'when type is NOT application' do
60
+ it 'is the name with underescores' do
61
+ subject = subject_class.new 'random/sub-type.with.dots'
62
+ expect(subject.to_sym).to eq(:random_sub_type_with_dots)
63
+ end
64
+ end
65
+ end
66
+
67
+ describe '#inspect' do
68
+ it 'is #<Swaggable::ContentTypeDefinition: type/subtype>' do
69
+ expect(subject.inspect).to eq('#<Swaggable::ContentTypeDefinition: application/json>')
70
+ end
71
+ end
72
+
73
+ describe '#==' do
74
+ it 'equals by name' do
75
+ type_1 = subject_class.new :json
76
+ type_1_again = subject_class.new :json
77
+ type_2 = subject_class.new :xml
78
+
79
+ expect(type_1).to eq type_1_again
80
+ expect([type_1, type_1_again].uniq.length).to eq 1
81
+ expect(type_1).not_to eq type_2
82
+ end
83
+
84
+ it 'equals by symbol' do
85
+ expect(subject == :json).to be true
86
+ end
87
+
88
+ it 'equals string' do
89
+ expect(subject == 'application/json').to be true
90
+ end
91
+
92
+ it 'doesn\'t throw error if comparing with any random object' do
93
+ expect{ subject == double }.not_to raise_error
94
+ end
95
+ end
96
+ end