swaggable 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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