apivore 0.0.2 → 1.0.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.
- checksums.yaml +13 -5
- data/lib/apivore.rb +8 -46
- data/lib/apivore/all_routes_tested_validator.rb +25 -0
- data/lib/apivore/fragment.rb +9 -0
- data/lib/apivore/rspec_helpers.rb +14 -0
- data/lib/apivore/rspec_matchers.rb +13 -46
- data/lib/apivore/swagger.rb +41 -0
- data/lib/apivore/swagger_checker.rb +89 -0
- data/lib/apivore/validator.rb +132 -0
- metadata +58 -67
- data/lib/apivore/rspec_builder.rb +0 -114
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YWNjOGI2MTJjZjgyM2M5MjRiYjA1ODg4YTA1MmRkOWY0YTlkYzlmNw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
N2JjZWIzYTlhN2UyNTNhNjJkMDZiYjIxNTA4NWYzY2FiNGYxM2RjYw==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
Y2NhZDZiYTdmNWNlYzYwMWRkZTVlMDkyZmQwMWI4OTdkNGE1NTU0YjY5Mzgy
|
10
|
+
ZGU4NWI3MjA4YjBjMWYwZWM3MDc0Mjk2ODc5YjRhODhhMmQxYzg0ODJjMWFl
|
11
|
+
Zjk4YzdhMTljMzI2YTRjNWMwMGNlY2RhNmM5NTZhNDlhNWI2YzA=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NDI1NjQyODRlYzZhZjFjN2UxODZmZGMzN2Y4MjBmMDc3NjI0Yzc2OTNhNTBm
|
14
|
+
M2Y1YjUxZGY0OWRmZGFiZTc0ZTcwZjVkZTFjOTI0MzU2YTgyMDEwYzA5YTY4
|
15
|
+
YzNkZmY1NDQ2MDE5Y2UxNDM2NGY3MzAyMzI4NWRiZDRkNDM2Yjc=
|
data/lib/apivore.rb
CHANGED
@@ -1,48 +1,10 @@
|
|
1
|
-
require 'apivore/rspec_builder'
|
2
1
|
require 'apivore/rspec_matchers'
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
else
|
12
|
-
raise "Unknown/unsupported Swagger version to validate against: #{version}"
|
13
|
-
end
|
14
|
-
JSON::Validator.fully_validate(schema, self)
|
15
|
-
end
|
16
|
-
|
17
|
-
def version
|
18
|
-
swagger
|
19
|
-
end
|
20
|
-
|
21
|
-
def base_path
|
22
|
-
self['basePath'] || ''
|
23
|
-
end
|
24
|
-
|
25
|
-
def each_response(&block)
|
26
|
-
paths.each do |path, path_data|
|
27
|
-
path_data.each do |verb, method_data|
|
28
|
-
raise "No responses found in swagger for path '#{path}', method #{verb}: #{method_data.inspect}" if method_data.responses.nil?
|
29
|
-
method_data.responses.each do |response_code, response_data|
|
30
|
-
schema_location = nil
|
31
|
-
if response_data.schema
|
32
|
-
schema_location = Fragment.new ['#', 'paths', path, verb, 'responses', response_code, 'schema']
|
33
|
-
end
|
34
|
-
block.call(path, verb, response_code, schema_location)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
# This is a workaround for json-schema's fragment validation which does not allow paths to contain forward slashes
|
42
|
-
# current json-schema attempts to split('/') on a string path to produce an array.
|
43
|
-
class Fragment < Array
|
44
|
-
def split(options = nil)
|
45
|
-
self
|
46
|
-
end
|
47
|
-
end
|
2
|
+
require 'apivore/rspec_helpers'
|
3
|
+
require 'apivore/swagger_checker'
|
4
|
+
require 'apivore/swagger'
|
5
|
+
require 'rspec'
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.include Apivore::RspecMatchers, type: :apivore
|
9
|
+
config.include Apivore::RspecHelpers, type: :apivore
|
48
10
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Apivore
|
2
|
+
class AllRoutesTestedValidator
|
3
|
+
|
4
|
+
def matches?(swagger_checker)
|
5
|
+
@errors = []
|
6
|
+
swagger_checker.mappings.each do |path, methods|
|
7
|
+
methods.each do |method, codes|
|
8
|
+
codes.each do |code, _|
|
9
|
+
@errors << "#{method} #{path} is untested for response code #{code}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
@errors.empty?
|
15
|
+
end
|
16
|
+
|
17
|
+
def description
|
18
|
+
"have tested all documented routes"
|
19
|
+
end
|
20
|
+
|
21
|
+
def failure_message
|
22
|
+
@errors.join("\n")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Apivore
|
2
|
+
# This is a workaround for json-schema's fragment validation which does not allow paths to contain forward slashes
|
3
|
+
# current json-schema attempts to split('/') on a string path to produce an array.
|
4
|
+
class Fragment < Array
|
5
|
+
def split(options = nil)
|
6
|
+
self
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'apivore/validator'
|
2
|
+
require 'apivore/all_routes_tested_validator'
|
3
|
+
|
4
|
+
module Apivore
|
5
|
+
module RspecHelpers
|
6
|
+
def validate(method, path, response_code, params = {})
|
7
|
+
Validator.new(method, path, response_code, params)
|
8
|
+
end
|
9
|
+
|
10
|
+
def validate_all_paths
|
11
|
+
AllRoutesTestedValidator.new
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -5,41 +5,11 @@ require 'net/http'
|
|
5
5
|
module Apivore
|
6
6
|
module RspecMatchers
|
7
7
|
extend RSpec::Matchers::DSL
|
8
|
-
matcher :
|
9
|
-
match do |body|
|
10
|
-
@api_description = Swagger.new(JSON.parse(body))
|
11
|
-
@errors = @api_description.validate
|
12
|
-
@errors.empty?
|
13
|
-
end
|
14
|
-
|
15
|
-
failure_message do |body|
|
16
|
-
msg = "The document fails to validate as Swagger #{@api_description.version}:\n"
|
17
|
-
msg += @errors.join("\n")
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
matcher :have_models_for_all_get_endpoints do
|
22
|
-
match do |body|
|
23
|
-
@errors = []
|
24
|
-
swagger = Swagger.new(JSON.parse(body))
|
25
|
-
swagger.each_response do |path, method, response_code, schema|
|
26
|
-
if method == 'get' && !schema
|
27
|
-
@errors << "Unable to find a valid model for #{path} get #{response_code} response."
|
28
|
-
end
|
29
|
-
end
|
30
|
-
@errors.empty?
|
31
|
-
end
|
32
|
-
|
33
|
-
failure_message do
|
34
|
-
@errors.join("\n")
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
matcher :be_consistent_with_swagger_definitions do |master_swagger, current_service|
|
8
|
+
matcher :be_consistent_with_swagger_definitions do |master_swagger_host, current_service|
|
39
9
|
|
40
10
|
attr_reader :actual, :expected
|
41
11
|
|
42
|
-
|
12
|
+
define_method :cleaned_definitions do |definitions, current_service|
|
43
13
|
definitions.each do |key, definition_fields|
|
44
14
|
# We ignore definitions that are owned exclusively by the current_service
|
45
15
|
if [current_service] == definition_fields['x-services']
|
@@ -52,8 +22,17 @@ module Apivore
|
|
52
22
|
end.select{ |_, value| !value.nil? }
|
53
23
|
end
|
54
24
|
|
55
|
-
|
56
|
-
|
25
|
+
define_method :fetch_master_swagger do
|
26
|
+
req = Net::HTTP.get(master_swagger_host, "/swagger.json")
|
27
|
+
JSON.parse(req)
|
28
|
+
end
|
29
|
+
|
30
|
+
define_method :master_swagger do
|
31
|
+
@master_swagger ||= fetch_master_swagger
|
32
|
+
end
|
33
|
+
|
34
|
+
match do |swagger_checker|
|
35
|
+
our_swagger = swagger_checker.swagger
|
57
36
|
master_definitions = cleaned_definitions(master_swagger["definitions"], current_service)
|
58
37
|
our_definitions = our_swagger["definitions"]
|
59
38
|
@actual = our_definitions.slice(*master_definitions.keys)
|
@@ -63,17 +42,5 @@ module Apivore
|
|
63
42
|
|
64
43
|
diffable
|
65
44
|
end
|
66
|
-
|
67
|
-
matcher :conform_to_the_documented_model_for do |swagger, fragment|
|
68
|
-
match do |body|
|
69
|
-
body = JSON.parse(body)
|
70
|
-
@errors = JSON::Validator.fully_validate(swagger, body, fragment: fragment)
|
71
|
-
@errors.empty?
|
72
|
-
end
|
73
|
-
|
74
|
-
failure_message do |body|
|
75
|
-
@errors.map { |e| e.sub("'#", "'#{path}#").gsub(/^The property|in schema.*$/,'') }.join("\n")
|
76
|
-
end
|
77
|
-
end
|
78
45
|
end
|
79
46
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
require 'apivore/fragment'
|
4
|
+
|
5
|
+
module Apivore
|
6
|
+
class Swagger < Hashie::Mash
|
7
|
+
|
8
|
+
def validate
|
9
|
+
case version
|
10
|
+
when '2.0'
|
11
|
+
schema = File.read(File.expand_path("../../../data/swagger_2.0_schema.json", __FILE__))
|
12
|
+
else
|
13
|
+
raise "Unknown/unsupported Swagger version to validate against: #{version}"
|
14
|
+
end
|
15
|
+
JSON::Validator.fully_validate(schema, self)
|
16
|
+
end
|
17
|
+
|
18
|
+
def version
|
19
|
+
swagger
|
20
|
+
end
|
21
|
+
|
22
|
+
def base_path
|
23
|
+
self['basePath'] || ''
|
24
|
+
end
|
25
|
+
|
26
|
+
def each_response(&block)
|
27
|
+
paths.each do |path, path_data|
|
28
|
+
path_data.each do |verb, method_data|
|
29
|
+
raise "No responses found in swagger for path '#{path}', method #{verb}: #{method_data.inspect}" if method_data.responses.nil?
|
30
|
+
method_data.responses.each do |response_code, response_data|
|
31
|
+
schema_location = nil
|
32
|
+
if response_data.schema
|
33
|
+
schema_location = Fragment.new ['#', 'paths', path, verb, 'responses', response_code, 'schema']
|
34
|
+
end
|
35
|
+
block.call(path, verb, response_code, schema_location)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Apivore
|
2
|
+
class SwaggerChecker
|
3
|
+
PATH_TO_CHECKER_MAP = {}
|
4
|
+
|
5
|
+
def self.instance_for(path)
|
6
|
+
PATH_TO_CHECKER_MAP[path] ||= new(path)
|
7
|
+
end
|
8
|
+
|
9
|
+
def has_path?(path)
|
10
|
+
mappings.has_key?(path)
|
11
|
+
end
|
12
|
+
|
13
|
+
def has_method_at_path?(path, method)
|
14
|
+
mappings[path].has_key?(method)
|
15
|
+
end
|
16
|
+
|
17
|
+
def has_response_code_for_path?(path, method, code)
|
18
|
+
mappings[path][method].has_key?(code.to_s)
|
19
|
+
end
|
20
|
+
|
21
|
+
def has_matching_document_for(path, method, code, body)
|
22
|
+
JSON::Validator.fully_validate(
|
23
|
+
swagger, body, fragment: fragment(path, method, code)
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def fragment(path, method, code)
|
28
|
+
mappings[path][method.to_s][code.to_s]
|
29
|
+
end
|
30
|
+
|
31
|
+
def remove_tested_end_point_response(path, method, code)
|
32
|
+
mappings[path][method].delete(code.to_s)
|
33
|
+
if mappings[path][method].size == 0
|
34
|
+
mappings[path].delete(method)
|
35
|
+
if mappings[path].size == 0
|
36
|
+
mappings.delete(path)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def base_path
|
42
|
+
@swagger.base_path
|
43
|
+
end
|
44
|
+
|
45
|
+
attr_reader :swagger_path, :mappings, :swagger
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def initialize(swagger_path)
|
50
|
+
@swagger_path = swagger_path
|
51
|
+
load_swagger_doc!
|
52
|
+
validate_swagger!
|
53
|
+
setup_mappings!
|
54
|
+
end
|
55
|
+
|
56
|
+
def load_swagger_doc!
|
57
|
+
@swagger = Apivore::Swagger.new(fetch_swagger!)
|
58
|
+
end
|
59
|
+
|
60
|
+
def fetch_swagger!
|
61
|
+
session = ActionDispatch::Integration::Session.new(Rails.application)
|
62
|
+
begin
|
63
|
+
session.get(swagger_path)
|
64
|
+
rescue
|
65
|
+
fail "Unable to perform GET request for swagger json: #{swagger_path} - #{$!}."
|
66
|
+
end
|
67
|
+
JSON.parse(session.response.body)
|
68
|
+
end
|
69
|
+
|
70
|
+
def validate_swagger!
|
71
|
+
errors = swagger.validate
|
72
|
+
unless errors.empty?
|
73
|
+
msg = "The document fails to validate as Swagger #{swagger.version}:\n"
|
74
|
+
msg += errors.join("\n")
|
75
|
+
fail msg
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def setup_mappings!
|
80
|
+
@mappings = {}
|
81
|
+
@swagger.each_response do |path, method, response_code, fragment|
|
82
|
+
@mappings[path] ||= {}
|
83
|
+
@mappings[path][method] ||= {}
|
84
|
+
raise "duplicate" unless @mappings[path][method][response_code].nil?
|
85
|
+
@mappings[path][method][response_code] = fragment
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'action_controller'
|
2
|
+
require 'action_dispatch'
|
3
|
+
|
4
|
+
module Apivore
|
5
|
+
class Validator
|
6
|
+
include ::ActionDispatch::Integration::Runner
|
7
|
+
|
8
|
+
attr_reader :method, :path, :expected_response_code, :params
|
9
|
+
|
10
|
+
def initialize(method, path, expected_response_code, params = {})
|
11
|
+
@method = method.to_s
|
12
|
+
@path = path.to_s
|
13
|
+
@params = params
|
14
|
+
@expected_response_code = expected_response_code.to_i
|
15
|
+
end
|
16
|
+
|
17
|
+
def matches?(swagger_checker)
|
18
|
+
pre_checks(swagger_checker)
|
19
|
+
|
20
|
+
unless has_errors?
|
21
|
+
send(
|
22
|
+
method,
|
23
|
+
full_path(swagger_checker),
|
24
|
+
params['_data'] || {},
|
25
|
+
params['_headers'] || {}
|
26
|
+
)
|
27
|
+
post_checks(swagger_checker)
|
28
|
+
swagger_checker.remove_tested_end_point_response(
|
29
|
+
path, method, expected_response_code
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
!has_errors?
|
34
|
+
end
|
35
|
+
|
36
|
+
def full_path(swagger_checker)
|
37
|
+
apivore_build_path(swagger_checker.base_path + path, params)
|
38
|
+
end
|
39
|
+
|
40
|
+
def apivore_build_path(path, data)
|
41
|
+
path.scan(/\{([^\}]*)\}/).each do |param|
|
42
|
+
key = param.first
|
43
|
+
if data && data[key]
|
44
|
+
path = path.gsub "{#{key}}", data[key].to_s
|
45
|
+
else
|
46
|
+
raise URI::InvalidURIError, "No substitution data found for {#{key}}"\
|
47
|
+
" to test the path #{path}.", caller
|
48
|
+
end
|
49
|
+
end
|
50
|
+
path + (data['_query_string'] ? "?#{data['_query_string']}" : '')
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def pre_checks(swagger_checker)
|
55
|
+
check_request_path(swagger_checker)
|
56
|
+
end
|
57
|
+
|
58
|
+
def post_checks(swagger_checker)
|
59
|
+
check_status_code
|
60
|
+
check_response_is_valid(swagger_checker) unless has_errors?
|
61
|
+
end
|
62
|
+
|
63
|
+
def check_request_path(swagger_checker)
|
64
|
+
if !swagger_checker.has_path?(path)
|
65
|
+
errors << "Swagger doc: #{swagger_checker.swagger_path} does not have"\
|
66
|
+
" a documented path for #{path}"
|
67
|
+
elsif !swagger_checker.has_method_at_path?(path, method)
|
68
|
+
errors << "Swagger doc: #{swagger_checker.swagger_path} does not have"\
|
69
|
+
" a documented path for #{method} #{path}"
|
70
|
+
elsif !swagger_checker.has_response_code_for_path?(path, method, expected_response_code)
|
71
|
+
errors << "Swagger doc: #{swagger_checker.swagger_path} does not have"\
|
72
|
+
" a documented response code of #{expected_response_code} at path"\
|
73
|
+
" #{method} #{path}"
|
74
|
+
elsif method == "get" && swagger_checker.fragment(path, method, expected_response_code).nil?
|
75
|
+
errors << "Swagger doc: #{swagger_checker.swagger_path} missing"\
|
76
|
+
" response model for get request with #{path} for code"/
|
77
|
+
" #{expected_response_code}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def check_status_code
|
82
|
+
if response.status != expected_response_code
|
83
|
+
errors << "Path #{path} did not respond with expected status code."\
|
84
|
+
" Expected #{expected_response_code} got #{response.status}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def check_response_is_valid(swagger_checker)
|
89
|
+
swagger_errors = swagger_checker.has_matching_document_for(
|
90
|
+
path, method, response.status, response_body
|
91
|
+
)
|
92
|
+
unless swagger_errors.empty?
|
93
|
+
errors.concat(
|
94
|
+
swagger_errors.map do |e|
|
95
|
+
e.sub("'#", "'#{full_path(swagger_checker)}#").gsub(
|
96
|
+
/^The property|in schema.*$/,''
|
97
|
+
)
|
98
|
+
end
|
99
|
+
)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def response_body
|
104
|
+
JSON.parse(response.body) unless response.body.blank?
|
105
|
+
end
|
106
|
+
|
107
|
+
def has_errors?
|
108
|
+
!errors.empty?
|
109
|
+
end
|
110
|
+
|
111
|
+
def failure_message
|
112
|
+
errors.join(" ")
|
113
|
+
end
|
114
|
+
|
115
|
+
def errors
|
116
|
+
@errors ||= []
|
117
|
+
end
|
118
|
+
|
119
|
+
def description
|
120
|
+
"validate that #{method} #{path} returns #{expected_response_code}"
|
121
|
+
end
|
122
|
+
|
123
|
+
# Required by ActionDispatch::Integration::Runner
|
124
|
+
def app
|
125
|
+
::Rails.application
|
126
|
+
end
|
127
|
+
|
128
|
+
# Required by rails
|
129
|
+
def reset_template_assertion
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: apivore
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Charles Horn
|
@@ -14,158 +14,144 @@ dependencies:
|
|
14
14
|
name: json-schema
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ~>
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 2.5
|
19
|
+
version: '2.5'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 2.5
|
26
|
+
version: '2.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rspec-expectations
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
|
-
- -
|
45
|
+
- - ~>
|
32
46
|
- !ruby/object:Gem::Version
|
33
47
|
version: '3.1'
|
34
48
|
type: :runtime
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
|
-
- -
|
52
|
+
- - ~>
|
39
53
|
- !ruby/object:Gem::Version
|
40
54
|
version: '3.1'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rspec-mocks
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
|
-
- -
|
59
|
+
- - ~>
|
46
60
|
- !ruby/object:Gem::Version
|
47
61
|
version: '3.1'
|
48
62
|
type: :runtime
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
|
-
- -
|
66
|
+
- - ~>
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '3.1'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: actionpack
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
|
-
- -
|
73
|
+
- - ~>
|
60
74
|
- !ruby/object:Gem::Version
|
61
75
|
version: '4'
|
62
76
|
type: :runtime
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
|
-
- -
|
80
|
+
- - ~>
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '4'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: hashie
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
|
-
- -
|
87
|
+
- - ~>
|
74
88
|
- !ruby/object:Gem::Version
|
75
|
-
version: 3.3
|
89
|
+
version: '3.3'
|
76
90
|
type: :runtime
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
|
-
- -
|
94
|
+
- - ~>
|
81
95
|
- !ruby/object:Gem::Version
|
82
|
-
version: 3.3
|
96
|
+
version: '3.3'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: pry
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
86
100
|
requirements:
|
87
|
-
- -
|
101
|
+
- - ~>
|
88
102
|
- !ruby/object:Gem::Version
|
89
103
|
version: '0'
|
90
104
|
type: :development
|
91
105
|
prerelease: false
|
92
106
|
version_requirements: !ruby/object:Gem::Requirement
|
93
107
|
requirements:
|
94
|
-
- -
|
108
|
+
- - ~>
|
95
109
|
- !ruby/object:Gem::Version
|
96
110
|
version: '0'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: rake
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
100
114
|
requirements:
|
101
|
-
- -
|
115
|
+
- - ~>
|
102
116
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
117
|
+
version: '10.3'
|
104
118
|
type: :development
|
105
119
|
prerelease: false
|
106
120
|
version_requirements: !ruby/object:Gem::Requirement
|
107
121
|
requirements:
|
108
|
-
- -
|
122
|
+
- - ~>
|
109
123
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: rspec
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - ">="
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - ">="
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: '0'
|
124
|
+
version: '10.3'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: rspec-rails
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- -
|
129
|
+
- - ~>
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: '
|
131
|
+
version: '3'
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
|
-
- -
|
136
|
+
- - ~>
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: '
|
138
|
+
version: '3'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: activesupport
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
|
-
- -
|
144
|
-
- !ruby/object:Gem::Version
|
145
|
-
version: '0'
|
146
|
-
type: :development
|
147
|
-
prerelease: false
|
148
|
-
version_requirements: !ruby/object:Gem::Requirement
|
149
|
-
requirements:
|
150
|
-
- - ">="
|
151
|
-
- !ruby/object:Gem::Version
|
152
|
-
version: '0'
|
153
|
-
- !ruby/object:Gem::Dependency
|
154
|
-
name: test-unit
|
155
|
-
requirement: !ruby/object:Gem::Requirement
|
156
|
-
requirements:
|
157
|
-
- - ">="
|
143
|
+
- - ~>
|
158
144
|
- !ruby/object:Gem::Version
|
159
|
-
version: '
|
145
|
+
version: '4'
|
160
146
|
type: :development
|
161
147
|
prerelease: false
|
162
148
|
version_requirements: !ruby/object:Gem::Requirement
|
163
149
|
requirements:
|
164
|
-
- -
|
150
|
+
- - ~>
|
165
151
|
- !ruby/object:Gem::Version
|
166
|
-
version: '
|
167
|
-
description:
|
168
|
-
|
152
|
+
version: '4'
|
153
|
+
description: Tests your rails API using its Swagger description of end-points, models,
|
154
|
+
and query parameters.
|
169
155
|
email: charles.horn@gmail.com
|
170
156
|
executables: []
|
171
157
|
extensions: []
|
@@ -173,10 +159,17 @@ extra_rdoc_files: []
|
|
173
159
|
files:
|
174
160
|
- data/swagger_2.0_schema.json
|
175
161
|
- lib/apivore.rb
|
176
|
-
- lib/apivore/
|
162
|
+
- lib/apivore/all_routes_tested_validator.rb
|
163
|
+
- lib/apivore/fragment.rb
|
164
|
+
- lib/apivore/rspec_helpers.rb
|
177
165
|
- lib/apivore/rspec_matchers.rb
|
166
|
+
- lib/apivore/swagger.rb
|
167
|
+
- lib/apivore/swagger_checker.rb
|
168
|
+
- lib/apivore/validator.rb
|
178
169
|
homepage: http://github.com/westfieldlabs/apivore
|
179
|
-
licenses:
|
170
|
+
licenses:
|
171
|
+
- Apache 2.0
|
172
|
+
- MIT
|
180
173
|
metadata: {}
|
181
174
|
post_install_message:
|
182
175
|
rdoc_options: []
|
@@ -184,20 +177,18 @@ require_paths:
|
|
184
177
|
- lib
|
185
178
|
required_ruby_version: !ruby/object:Gem::Requirement
|
186
179
|
requirements:
|
187
|
-
- -
|
180
|
+
- - ! '>='
|
188
181
|
- !ruby/object:Gem::Version
|
189
182
|
version: '0'
|
190
183
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
191
184
|
requirements:
|
192
|
-
- -
|
185
|
+
- - ! '>='
|
193
186
|
- !ruby/object:Gem::Version
|
194
187
|
version: '0'
|
195
188
|
requirements: []
|
196
189
|
rubyforge_project:
|
197
|
-
rubygems_version: 2.4.
|
190
|
+
rubygems_version: 2.4.5
|
198
191
|
signing_key:
|
199
192
|
specification_version: 4
|
200
|
-
summary:
|
201
|
-
models, and query parameters.
|
193
|
+
summary: Tests your API against its Swagger 2.0 spec
|
202
194
|
test_files: []
|
203
|
-
has_rdoc:
|
@@ -1,114 +0,0 @@
|
|
1
|
-
require 'apivore/rspec_matchers'
|
2
|
-
require 'action_controller'
|
3
|
-
require 'action_dispatch'
|
4
|
-
require 'rspec/mocks'
|
5
|
-
require 'hashie'
|
6
|
-
|
7
|
-
module Apivore
|
8
|
-
module RspecBuilder
|
9
|
-
include Apivore::RspecMatchers
|
10
|
-
include ActionDispatch::Integration
|
11
|
-
include RSpec::Mocks::ExampleMethods
|
12
|
-
|
13
|
-
@@setups ||= {}
|
14
|
-
|
15
|
-
@@master_swagger_uri = nil
|
16
|
-
|
17
|
-
# Setup tests against a combination of path, method, and response.
|
18
|
-
# - *keys -> A combination of path, method, and/or response. Blank '' for base setup.
|
19
|
-
# - &block -> Code block to execute to setup the test. A hash of path subsitution parameters can be returned if required.
|
20
|
-
# All matching code blocks are executed, and substitution parameters are merged in order of specificity.
|
21
|
-
def apivore_setup(*keys, &block)
|
22
|
-
@@setups[keys.join] = block
|
23
|
-
end
|
24
|
-
|
25
|
-
def get_apivore_setup(path, method, response)
|
26
|
-
keys_to_search = [
|
27
|
-
'', # base setup key
|
28
|
-
response,
|
29
|
-
method,
|
30
|
-
path,
|
31
|
-
method + response,
|
32
|
-
path + response,
|
33
|
-
path + method,
|
34
|
-
path + method + response
|
35
|
-
]
|
36
|
-
final_result = {}
|
37
|
-
keys_to_search.each do |k|
|
38
|
-
setup = @@setups[k]
|
39
|
-
if setup
|
40
|
-
result = instance_eval &setup
|
41
|
-
final_result.merge!(result) if result.is_a?(Hash)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
final_result
|
45
|
-
end
|
46
|
-
|
47
|
-
def apivore_build_path(path, data)
|
48
|
-
path.scan(/\{([^\}]*)\}/).each do |param|
|
49
|
-
key = param.first
|
50
|
-
if data && data[key]
|
51
|
-
path = path.gsub "{#{key}}", data[key].to_s
|
52
|
-
else
|
53
|
-
raise URI::InvalidURIError, "No substitution data found for {#{key}} to test the path #{path}.\nAdd it via an:\n apivore_setup '<path>', '<method>', '<response>' do\n { '#{key}' => <value> }\n end\nblock in your specs.", caller
|
54
|
-
end
|
55
|
-
end
|
56
|
-
path + (data['_query_string'] ? "?#{data['_query_string']}" : '')
|
57
|
-
end
|
58
|
-
|
59
|
-
def apivore_check_consistency_with_swagger_at(uri, current_service = nil)
|
60
|
-
@@current_service = current_service
|
61
|
-
@@master_swagger_uri = uri
|
62
|
-
end
|
63
|
-
|
64
|
-
def apivore_swagger(swagger_path)
|
65
|
-
session = ActionDispatch::Integration::Session.new(Rails.application)
|
66
|
-
begin
|
67
|
-
session.get swagger_path
|
68
|
-
rescue
|
69
|
-
# TODO: make this fail inside rspec test execution rather than immediately raise an exception.
|
70
|
-
# ALSO, handle other scenarios where we can't get a response to generate tests, e.g 500s, invalid formats etc
|
71
|
-
raise "Unable to perform GET request for swagger json: #{swagger_path} - #{$!}."
|
72
|
-
end
|
73
|
-
Apivore::Swagger.new JSON.parse(session.response.body)
|
74
|
-
end
|
75
|
-
|
76
|
-
def validate(swagger_path)
|
77
|
-
|
78
|
-
describe "swagger documentation" do
|
79
|
-
before { get swagger_path }
|
80
|
-
subject { body }
|
81
|
-
it { should be_valid_swagger }
|
82
|
-
it { should have_models_for_all_get_endpoints }
|
83
|
-
if @@master_swagger_uri
|
84
|
-
req = Net::HTTP.get(@@master_swagger_uri, "/swagger.json")
|
85
|
-
master_swagger = JSON.parse(req)
|
86
|
-
it { should be_consistent_with_swagger_definitions master_swagger, @@current_service }
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
swagger = apivore_swagger(swagger_path)
|
91
|
-
swagger.each_response do |path, method, response_code, fragment|
|
92
|
-
describe "path #{path} method #{method} response #{response_code}" do
|
93
|
-
it "responds with the specified models" do
|
94
|
-
setup_data = get_apivore_setup(path, method, response_code)
|
95
|
-
full_path = apivore_build_path(swagger.base_path + path, setup_data)
|
96
|
-
# e.g., get(full_path)
|
97
|
-
begin
|
98
|
-
send(method, full_path, setup_data['_data'] || {}, setup_data['_headers'] || {})
|
99
|
-
rescue
|
100
|
-
raise "Unable to #{method} #{full_path} -- invalid response from server: #{$!}."
|
101
|
-
end
|
102
|
-
expect(response).to have_http_status(response_code), "expected #{response_code} array, got #{response.status}: #{response.body}"
|
103
|
-
|
104
|
-
if fragment
|
105
|
-
expect(response.body).to conform_to_the_documented_model_for(swagger, fragment)
|
106
|
-
end
|
107
|
-
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|