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 CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: e65e5c0dae07a51ff67726b32b3e6a244d3f4a88
4
- data.tar.gz: bd8bf1e39471e1671044321177cb12230a0a04e1
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YWNjOGI2MTJjZjgyM2M5MjRiYjA1ODg4YTA1MmRkOWY0YTlkYzlmNw==
5
+ data.tar.gz: !binary |-
6
+ N2JjZWIzYTlhN2UyNTNhNjJkMDZiYjIxNTA4NWYzY2FiNGYxM2RjYw==
5
7
  SHA512:
6
- metadata.gz: 9b72501bc2be6fb51da035c5527ed4d9c0b2518820e4801ad8b53e9c111aa0aee5ef8d3a3ecd456a7a5db88968536385bdbeba19c88174886f138e143a9a4a5e
7
- data.tar.gz: f6b065b7d68128e4d36206555c5b5d96864ea85adf9163775bb00c1d40f7fcf1bc8362908c1e72dfe3ad3996a170e408e0d503b6079ceef1d16138f431574b97
8
+ metadata.gz: !binary |-
9
+ Y2NhZDZiYTdmNWNlYzYwMWRkZTVlMDkyZmQwMWI4OTdkNGE1NTU0YjY5Mzgy
10
+ ZGU4NWI3MjA4YjBjMWYwZWM3MDc0Mjk2ODc5YjRhODhhMmQxYzg0ODJjMWFl
11
+ Zjk4YzdhMTljMzI2YTRjNWMwMGNlY2RhNmM5NTZhNDlhNWI2YzA=
12
+ data.tar.gz: !binary |-
13
+ NDI1NjQyODRlYzZhZjFjN2UxODZmZGMzN2Y4MjBmMDc3NjI0Yzc2OTNhNTBm
14
+ M2Y1YjUxZGY0OWRmZGFiZTc0ZTcwZjVkZTFjOTI0MzU2YTgyMDEwYzA5YTY4
15
+ YzNkZmY1NDQ2MDE5Y2UxNDM2NGY3MzAyMzI4NWRiZDRkNDM2Yjc=
@@ -1,48 +1,10 @@
1
- require 'apivore/rspec_builder'
2
1
  require 'apivore/rspec_matchers'
3
-
4
- module Apivore
5
- class Swagger < Hashie::Mash
6
-
7
- def validate
8
- case version
9
- when '2.0'
10
- schema = File.read(File.expand_path("../../data/swagger_2.0_schema.json", __FILE__))
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 :be_valid_swagger do |version|
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
- def cleaned_definitions(definitions, current_service)
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
- match do |body|
56
- our_swagger = JSON.parse(body)
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.2
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.1
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.1
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.1
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.1
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: '0'
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: '0'
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: '0'
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: '0'
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: '0'
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: '0'
167
- description: Automatically tests your API using its Swagger description of end-points,
168
- models, and query parameters.
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/rspec_builder.rb
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.6
190
+ rubygems_version: 2.4.5
198
191
  signing_key:
199
192
  specification_version: 4
200
- summary: Automatically tests your API using its Swagger description of end-points,
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