swaggable 0.4.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.
@@ -0,0 +1,100 @@
1
+ require_relative '../spec_helper'
2
+ require 'rack/test'
3
+ require 'grape'
4
+
5
+ RSpec.describe 'Integration' do
6
+ context 'RackApp' do
7
+ include Rack::Test::Methods
8
+
9
+ def app
10
+ subject
11
+ end
12
+
13
+ subject { rack_app }
14
+ let(:rack_app) { Swaggable::RackApp.new(api_definition: api_def) }
15
+ let(:api_def) { Swaggable::ApiDefinition.from_grape_api(grape_api) }
16
+
17
+ let(:grape_api) do
18
+ Class.new(Grape::API).tap do |g|
19
+ g.params do
20
+ requires :user_uuid
21
+ end
22
+
23
+ g.get '/' do
24
+ status 200
25
+ body({some: 'body'})
26
+ end
27
+
28
+ end.tap do |grape|
29
+ stub_const('MyGrapeApi', grape)
30
+ end
31
+ end
32
+
33
+ it 'has status 200 OK' do
34
+ get '/'
35
+ expect(last_response.status).to eq 200
36
+ expect(last_response.headers['Content-Type']).to eq 'application/json'
37
+ end
38
+
39
+ it 'validates' do
40
+ expect(subject.validate!).to be true
41
+ end
42
+ end
43
+
44
+ context 'dsl' do
45
+ it 'supports a full description of the API' do
46
+ api = Swaggable::ApiDefinition.new do
47
+ version '1.0'
48
+ title 'My API'
49
+ description 'A test API'
50
+ base_path '/api/1.0'
51
+
52
+ endpoints.add_new do
53
+ path '/users/{id}'
54
+ verb :get
55
+ description 'Shows an user'
56
+ summary 'Returns the JSON representation of such user'
57
+
58
+ tags.add_new do
59
+ name 'Users'
60
+ description 'Users resource'
61
+ end
62
+
63
+ parameters.add_new do
64
+ name 'include_comments_count'
65
+ description 'It will return the comments_count attribute when set to true'
66
+ location :query # [:path, :query, :header, :body, :form, nil]
67
+ required false
68
+ type :boolean # [:string, :number, :integer, :boolean, :array, :file, nil]
69
+ end
70
+
71
+ responses.add_new do
72
+ status 200
73
+ description 'Success'
74
+ end
75
+
76
+ responses.add_new do
77
+ status 404
78
+ description 'User not found'
79
+ end
80
+
81
+ consumes << :json
82
+ produces << :json
83
+ end
84
+ end
85
+
86
+ expect(api.version).to eq '1.0'
87
+ expect(api.endpoints.first).to be api.endpoints['GET /users/{id}']
88
+ expect(api.endpoints.first.path).to eq '/users/{id}'
89
+ expect(api.endpoints.first.tags.first).to be api.endpoints.first.tags['Users']
90
+ expect(api.endpoints.first.tags.first.name).to eq 'Users'
91
+ expect(api.endpoints.first.parameters.first).to be api.endpoints.first.parameters['include_comments_count']
92
+ expect(api.endpoints.first.parameters.first.name).to eq 'include_comments_count'
93
+ expect(api.endpoints.first.responses.first).to be api.endpoints.first.responses[200]
94
+ expect(api.endpoints.first.responses.first.description).to eq 'Success'
95
+ expect(api.endpoints.first.consumes).to eq [:json]
96
+ expect(api.endpoints.first.produces).to eq [:json]
97
+ end
98
+ end
99
+ end
100
+
@@ -0,0 +1,64 @@
1
+ require_relative '../spec_helper'
2
+
3
+ RSpec.describe 'Swaggable::ParameterDefinition' do
4
+ let(:subject_class) { Swaggable::ParameterDefinition }
5
+ let(:subject_instance) { Swaggable::ParameterDefinition.new }
6
+ subject { subject_instance }
7
+
8
+ it 'has a name' do
9
+ subject.name = 'a name'
10
+ expect(subject.name).to eq 'a name'
11
+ end
12
+
13
+ it 'has a description' do
14
+ subject.description = 'a new desc'
15
+ expect(subject.description).to eq 'a new desc'
16
+ end
17
+
18
+ it 'has a required option' do
19
+ subject.required = true
20
+ expect(subject.required?).to be true
21
+ end
22
+
23
+ it 'yields itself on initialize' do
24
+ yielded = false
25
+
26
+ subject_class.new do |s|
27
+ expect(s).to be_a subject_class
28
+ yielded = true
29
+ end
30
+
31
+ expect(yielded).to be true
32
+ end
33
+
34
+ describe '#location' do
35
+ it 'can be set to :body, :header, :path, :query, :form' do
36
+ [:body, :header, :path, :query, :form, nil].each do |location|
37
+ subject.location = location
38
+ expect(subject.location).to eq location
39
+ end
40
+ end
41
+
42
+ it 'cannot be something other than that' do
43
+ expect { subject.location = :xyz }.to raise_exception
44
+ end
45
+ end
46
+
47
+ describe '#type' do
48
+ it 'can be set to :string, :number, :integer, :boolean, :array, :file' do
49
+ [:string, :number, :integer, :boolean, :array, :file, nil].each do |type|
50
+ subject.type = type
51
+ expect(subject.type).to eq type
52
+ end
53
+ end
54
+
55
+ it 'cannot be something other than that' do
56
+ expect { subject.type = :xyz }.to raise_exception
57
+ end
58
+ end
59
+
60
+ it 'accepts attributes on initialize' do
61
+ parameter = subject_class.new location: :path
62
+ expect(parameter.location).to eq :path
63
+ end
64
+ end
@@ -0,0 +1,49 @@
1
+ require_relative '../spec_helper'
2
+ require 'rack/test'
3
+
4
+ RSpec.describe 'Swaggable::RackApp' do
5
+ include Rack::Test::Methods
6
+
7
+ let(:subject_class) { Swaggable::RackApp }
8
+ let(:subject_instance) { Swaggable::RackApp.new serializer: serializer, api_definition: api_definition }
9
+ subject { subject_instance }
10
+ let(:api_definition) { instance_double(Swaggable::ApiDefinition) }
11
+ let(:serializer) { instance_double(Swaggable::Swagger2Serializer, serialize: {}) }
12
+
13
+ def app
14
+ subject_instance
15
+ end
16
+
17
+ it 'has status 200 OK' do
18
+ get '/'
19
+ expect(last_response.status).to eq 200
20
+ end
21
+
22
+ it 'has content_type application/json' do
23
+ get '/'
24
+ expect(last_response.headers['Content-Type']).to eq 'application/json'
25
+ end
26
+
27
+ it 'has body the serialized swagger' do
28
+ allow(serializer).to receive(:serialize).with(api_definition).and_return({some: :json})
29
+
30
+ get '/'
31
+ expect(last_response.body).to eq '{"some":"json"}'
32
+ end
33
+
34
+ it 'uses Swagger2Serializer by default' do
35
+ subject = subject_class.new
36
+ expect(subject.serializer).to be_a Swaggable::Swagger2Serializer
37
+ end
38
+
39
+ describe 'validate!' do
40
+ it 'validates against the serializer' do
41
+ expect(serializer).
42
+ to receive(:validate!).
43
+ with api_definition
44
+
45
+ subject.validate!
46
+ end
47
+ end
48
+ end
49
+
@@ -0,0 +1,17 @@
1
+ require_relative '../spec_helper'
2
+
3
+ RSpec.describe 'Swaggable::ResponseDefinition' do
4
+ let(:subject_class) { Swaggable::ResponseDefinition }
5
+ let(:subject_instance) { Swaggable::ResponseDefinition.new }
6
+ subject { subject_instance }
7
+
8
+ it 'has a status' do
9
+ subject.status = 418
10
+ expect(subject.status).to eq 418
11
+ end
12
+
13
+ it 'has a description' do
14
+ subject.description = "I'm a teapot"
15
+ expect(subject.description).to eq "I'm a teapot"
16
+ end
17
+ end
@@ -0,0 +1,208 @@
1
+ require_relative '../spec_helper'
2
+
3
+ RSpec.describe 'Swaggable::Swagger2Serializer' do
4
+ let(:subject_class) { Swaggable::Swagger2Serializer }
5
+ let(:subject_instance) { Swaggable::Swagger2Serializer.new }
6
+ subject { subject_instance }
7
+
8
+ let(:api) { Swaggable::ApiDefinition.new }
9
+
10
+ describe '#serialize' do
11
+ def output
12
+ subject.serialize(api)
13
+ end
14
+
15
+ it 'has swagger version 2.0' do
16
+ expect(output[:swagger]).to eq '2.0'
17
+ end
18
+
19
+ it 'has basePath' do
20
+ api.base_path = '/a/path'
21
+ expect(output[:basePath]).to eq '/a/path'
22
+ end
23
+
24
+ it 'has title' do
25
+ api.title = 'A Title'
26
+ expect(output[:info][:title]).to eq 'A Title'
27
+ end
28
+
29
+ it 'has title empty string if nil' do
30
+ api.title = nil
31
+ expect(output[:info][:title]).to eq ''
32
+ end
33
+
34
+ it 'has description' do
35
+ api.description = 'A Desc'
36
+ expect(output[:info][:description]).to eq 'A Desc'
37
+ end
38
+
39
+ it 'has no description if nil' do
40
+ api.description = nil
41
+ expect(output[:info].has_key?(:description)).to be false
42
+ end
43
+
44
+ it 'has version' do
45
+ api.version = 'v1.0'
46
+ expect(output[:info][:version]).to eq 'v1.0'
47
+ end
48
+
49
+ it 'has version "0.0.0" if nil' do
50
+ api.version = nil
51
+ expect(output[:info][:version]).to eq "0.0.0"
52
+ end
53
+
54
+ describe '#tags' do
55
+ def serialized_tag
56
+ output[:tags].first
57
+ end
58
+
59
+ let(:api) { Swaggable::ApiDefinition.new {|a| a.endpoints << endpoint } }
60
+ let(:endpoint) { Swaggable::EndpointDefinition.new {|e| e.tags << tag } }
61
+ let(:tag) { Swaggable::TagDefinition.new name: 'A Tag', description: 'A tag description' }
62
+
63
+ it 'has a name' do
64
+ expect(serialized_tag[:name]).to eq tag.name
65
+ end
66
+
67
+ it 'has a description' do
68
+ expect(serialized_tag[:description]).to eq tag.description
69
+ end
70
+
71
+ it 'has no description if nil' do
72
+ tag.description = nil
73
+ expect(serialized_tag.has_key?(:description)).to be false
74
+ end
75
+ end
76
+
77
+ describe '#endpoints' do
78
+ let(:api) { Swaggable::ApiDefinition.new {|a| a.endpoints << endpoint } }
79
+ let(:endpoint) { Swaggable::EndpointDefinition.new path: path, verb: verb }
80
+ let(:path) { '/a/path' }
81
+ let(:verb) { 'POST' }
82
+
83
+ it 'uses the path as key' do
84
+ expect(output[:paths][path]).not_to be_nil
85
+ end
86
+
87
+ it 'uses the verb as key' do
88
+ expect(output[:paths][path][verb]).not_to be_nil
89
+ end
90
+
91
+ def serialized_endpoint
92
+ output[:paths][path][verb]
93
+ end
94
+
95
+ it 'has description' do
96
+ endpoint.description = 'A desc.'
97
+ expect(serialized_endpoint[:description]).to eq 'A desc.'
98
+ end
99
+
100
+ it 'has no description if nil' do
101
+ endpoint.description = nil
102
+ expect(serialized_endpoint.has_key?(:description)).to be false
103
+ end
104
+
105
+ it 'has summary' do
106
+ endpoint.summary = 'A summary.'
107
+ expect(serialized_endpoint[:summary]).to eq 'A summary.'
108
+ end
109
+
110
+ it 'has no summary if summary is nil' do
111
+ endpoint.summary = nil
112
+ expect(serialized_endpoint.has_key?(:summary)).to be false
113
+ end
114
+
115
+ it 'has tags' do
116
+ endpoint.tags << Swaggable::TagDefinition.new(name: 'Tag 1')
117
+ endpoint.tags << Swaggable::TagDefinition.new(name: 'Tag 2')
118
+ expect(serialized_endpoint[:tags]).to eq ['Tag 1', 'Tag 2']
119
+ end
120
+
121
+ it 'has consumes' do
122
+ endpoint.consumes << 'application/whatever'
123
+ expect(serialized_endpoint[:consumes]).to eq ['application/whatever']
124
+ end
125
+
126
+ it 'has produces' do
127
+ endpoint.produces << 'application/whatever'
128
+ expect(serialized_endpoint[:produces]).to eq ['application/whatever']
129
+ end
130
+
131
+ it 'works with two endpoints with the same path' do
132
+ api.endpoints << Swaggable::EndpointDefinition.new(path: path, verb: 'get')
133
+
134
+ expect(output[:paths][path][verb]).not_to be_nil
135
+ expect(output[:paths][path]['get']).not_to be_nil
136
+ end
137
+
138
+ describe 'responses' do
139
+ it 'defaults to 200 Success' do
140
+ expect(serialized_endpoint[:responses]).to eq({200 => {description: "Success"}})
141
+ end
142
+
143
+ it 'is taken from the responses' do
144
+ endpoint.responses.add_new do
145
+ status 418
146
+ description 'Teapot'
147
+ end
148
+
149
+ expect(serialized_endpoint[:responses]).to eq({418 => {description: "Teapot"}})
150
+ end
151
+ end
152
+
153
+ describe 'parameters' do
154
+ before(:each) { endpoint.parameters << parameter }
155
+ let(:parameter) { Swaggable::ParameterDefinition.new location: :query }
156
+
157
+ def serialized_parameter
158
+ serialized_endpoint[:parameters].first
159
+ end
160
+
161
+ it 'has "in"' do
162
+ expect(serialized_parameter[:in]).to eq 'query'
163
+ end
164
+
165
+ it 'has name' do
166
+ parameter.name = 'my_param'
167
+ expect(serialized_parameter[:name]).to eq 'my_param'
168
+ end
169
+
170
+ it 'has description' do
171
+ parameter.description = 'A param'
172
+ expect(serialized_parameter[:description]).to eq 'A param'
173
+ end
174
+
175
+ it 'has no description if nil' do
176
+ parameter.description = nil
177
+ expect(serialized_parameter.has_key?(:description)).to be false
178
+ end
179
+
180
+ it 'has required' do
181
+ parameter.required = nil
182
+ expect(serialized_parameter[:required]).to eq false
183
+ end
184
+
185
+ it 'has type if present' do
186
+ parameter.type = :string
187
+ expect(serialized_parameter[:type]).to eq :string
188
+ end
189
+
190
+ it 'has type string if nil' do
191
+ parameter.type = nil
192
+ expect(serialized_parameter[:type]).to eq 'string'
193
+ end
194
+ end
195
+ end
196
+ end
197
+
198
+ describe '#validate!' do
199
+ it 'validates against Swagger2Validator' do
200
+ expect(Swaggable::Swagger2Validator).
201
+ to receive(:validate!).
202
+ with(subject.serialize api).
203
+ and_return(true)
204
+
205
+ subject.validate! api
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,26 @@
1
+ require_relative '../spec_helper'
2
+ require 'json'
3
+ require 'json-schema'
4
+
5
+ RSpec.describe 'Swaggable::Swagger2Validator' do
6
+ describe '.validate!' do
7
+ subject { Swaggable::Swagger2Validator }
8
+
9
+ let(:valid_swagger) { JSON.parse File.read('spec/assets/valid-swagger-2.0.json') }
10
+ let(:invalid_swagger) { valid_swagger.merge("info" => nil) }
11
+
12
+ it 'returns true for a valid schema' do
13
+ expect(subject.validate! valid_swagger).to be true
14
+ end
15
+
16
+ it 'raises exception true for an invalid schema' do
17
+ expect{ subject.validate! invalid_swagger }.
18
+ to raise_error(JSON::Schema::ValidationError)#(Swaggable::Swagger2Validator::ValidationError)
19
+ end
20
+
21
+ it 'has a meaningful exception message' do
22
+ exception = subject.validate!(invalid_swagger) rescue $!
23
+ expect(exception.message).to match(/info/)
24
+ end
25
+ end
26
+ end