rspec_api_blueprint_matchers 0.1.2 → 0.1.3
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 +4 -4
- data/.codeclimate.yml +33 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +26 -0
- data/.ruby-version +1 -0
- data/Rakefile +2 -1
- data/config/rubocop/.lint_rubocop.yml +749 -0
- data/config/rubocop/.metrics_rubocop.yml +94 -0
- data/config/rubocop/.performance_rubocop.yml +323 -0
- data/config/rubocop/.rails_rubocop.yml +256 -0
- data/config/rubocop/.style_rubocop.yml +2299 -0
- data/docker-compose.yml +17 -0
- data/lib/rspec_api_blueprint_matchers.rb +1 -0
- data/lib/rspec_apib/config.rb +1 -0
- data/lib/rspec_apib/elements/annotation.rb +1 -0
- data/lib/rspec_apib/elements/array.rb +1 -0
- data/lib/rspec_apib/elements/asset.rb +1 -0
- data/lib/rspec_apib/elements/base.rb +11 -10
- data/lib/rspec_apib/elements/category.rb +2 -1
- data/lib/rspec_apib/elements/copy.rb +2 -1
- data/lib/rspec_apib/elements/data_structure.rb +2 -0
- data/lib/rspec_apib/elements/href_variables.rb +3 -2
- data/lib/rspec_apib/elements/http_headers.rb +5 -4
- data/lib/rspec_apib/elements/http_message_payload.rb +3 -2
- data/lib/rspec_apib/elements/http_request.rb +16 -10
- data/lib/rspec_apib/elements/http_response.rb +3 -1
- data/lib/rspec_apib/elements/http_transaction.rb +4 -3
- data/lib/rspec_apib/elements/member.rb +3 -2
- data/lib/rspec_apib/elements/object.rb +2 -1
- data/lib/rspec_apib/elements/parse_result.rb +1 -0
- data/lib/rspec_apib/elements/resource.rb +1 -0
- data/lib/rspec_apib/elements/source_map.rb +1 -0
- data/lib/rspec_apib/elements/string.rb +1 -0
- data/lib/rspec_apib/elements/templated_href.rb +1 -0
- data/lib/rspec_apib/elements/transition.rb +1 -0
- data/lib/rspec_apib/elements.rb +1 -0
- data/lib/rspec_apib/extractors/http_transaction.rb +1 -0
- data/lib/rspec_apib/extractors/resource.rb +1 -0
- data/lib/rspec_apib/extractors.rb +1 -0
- data/lib/rspec_apib/parser.rb +2 -1
- data/lib/rspec_apib/request.rb +7 -10
- data/lib/rspec_apib/response.rb +2 -2
- data/lib/rspec_apib/rspec.rb +4 -3
- data/lib/rspec_apib/transaction_coverage_report.rb +4 -3
- data/lib/rspec_apib/transaction_coverage_validator.rb +4 -3
- data/lib/rspec_apib/transaction_validator.rb +1 -0
- data/lib/rspec_apib/transcluder.rb +2 -2
- data/lib/rspec_apib/version.rb +2 -1
- data/lib/rspec_apib.rb +2 -1
- data/lib/transcluder.rb +1 -0
- data/rspec_api_blueprint_matchers.gemspec +6 -4
- metadata +25 -2
data/docker-compose.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
version: '2'
|
2
|
+
|
3
|
+
services:
|
4
|
+
test:
|
5
|
+
image: rspec_api_blueprint_matchers
|
6
|
+
build:
|
7
|
+
context: .
|
8
|
+
dockerfile: Dockerfile
|
9
|
+
command: bash -c "bundle install && bundle exec rspec"
|
10
|
+
volumes:
|
11
|
+
- .:/app
|
12
|
+
- rubygems_cache:/rubygems
|
13
|
+
environment:
|
14
|
+
GEM_HOME: '/rubygems'
|
15
|
+
BUNDLE_PATH: '/rubygems'
|
16
|
+
volumes:
|
17
|
+
rubygems_cache:
|
data/lib/rspec_apib/config.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module RSpecApib
|
2
3
|
module Element
|
3
|
-
|
4
|
+
BaseStruct = Struct.new(:element, :meta, :attributes, :content, :parent)
|
4
5
|
|
5
|
-
|
6
|
+
# A base class for all objects in api-elements (http://api-elements.readthedocs.io/en/latest/)
|
7
|
+
class Base < BaseStruct
|
8
|
+
def inspect
|
6
9
|
"##{self.class} (element: #{element}, meta: #{meta}, attributes: #{attributes.inspect}, content: #{content.inspect}, parent: ##{parent.class})"
|
7
10
|
end
|
8
11
|
|
@@ -14,11 +17,11 @@ module RSpecApib
|
|
14
17
|
|
15
18
|
def self.parse(node_or_nodes, index:, parent:, klass: nil)
|
16
19
|
return node_or_nodes.map { |node| parse(node, index: index, parent: parent) } if node_or_nodes.is_a?(::Array)
|
17
|
-
return transformed_basic_hash(node_or_nodes, index: index, parent: parent) if
|
18
|
-
return node_or_nodes unless !klass.nil? ||
|
20
|
+
return transformed_basic_hash(node_or_nodes, index: index, parent: parent) if basic_hash?(node_or_nodes)
|
21
|
+
return node_or_nodes unless !klass.nil? || base_element?(node_or_nodes)
|
19
22
|
hash = node_or_nodes
|
20
23
|
klass_name = klass
|
21
|
-
klass_name ||= hash["element"].slice(0,1).capitalize + hash["element"].slice(1..-1).
|
24
|
+
klass_name ||= hash["element"].slice(0, 1).capitalize + hash["element"].slice(1..-1).delete(" ")
|
22
25
|
return node_or_nodes unless RSpecApib::Element.const_defined?(klass_name)
|
23
26
|
klass = RSpecApib::Element.const_get(klass_name)
|
24
27
|
index[klass] ||= []
|
@@ -61,12 +64,10 @@ module RSpecApib
|
|
61
64
|
{}
|
62
65
|
end
|
63
66
|
|
64
|
-
def self.
|
67
|
+
def self.base_element?(node)
|
65
68
|
node.is_a?(::Hash) && node.keys.include?("element")
|
66
69
|
end
|
67
70
|
|
68
|
-
private
|
69
|
-
|
70
71
|
def self.attributes_schema
|
71
72
|
{}
|
72
73
|
end
|
@@ -81,8 +82,8 @@ module RSpecApib
|
|
81
82
|
node
|
82
83
|
end
|
83
84
|
|
84
|
-
def self.
|
85
|
-
node.is_a?(::Hash) && !
|
85
|
+
def self.basic_hash?(node)
|
86
|
+
node.is_a?(::Hash) && !base_element?(node)
|
86
87
|
end
|
87
88
|
|
88
89
|
def self.transformed_basic_hash(node, index:, parent:)
|
@@ -1,13 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "addressable"
|
2
3
|
module RSpecApib
|
3
4
|
module Element
|
5
|
+
# Represents a collection of href variables in api-elements (http://api-elements.readthedocs.io/en/latest/)
|
4
6
|
class HrefVariables < Base
|
5
7
|
def [](key)
|
6
|
-
member = content.find {|h| h.is_a?(Member) && h.content.key?(key) }
|
8
|
+
member = content.find { |h| h.is_a?(Member) && h.content.key?(key) }
|
7
9
|
return nil if member.nil?
|
8
10
|
member.content[key]
|
9
11
|
end
|
10
|
-
|
11
12
|
end
|
12
13
|
end
|
13
14
|
end
|
@@ -1,14 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module RSpecApib
|
2
3
|
module Element
|
4
|
+
# Represents a collection of http headers in api-elements (http://api-elements.readthedocs.io/en/latest/)
|
3
5
|
class HttpHeaders < Base
|
4
6
|
def [](key)
|
5
|
-
member = content.find {|h| h.is_a?(Member) && h.content.key?(key) }
|
7
|
+
member = content.find { |h| h.is_a?(Member) && h.content.key?(key) }
|
6
8
|
return nil if member.nil?
|
7
9
|
member.content[key]
|
8
10
|
end
|
9
11
|
|
10
12
|
def each_pair
|
11
|
-
content.select {|h| h.is_a?(Member)}.each do |header|
|
13
|
+
content.select { |h| h.is_a?(Member) }.each do |header|
|
12
14
|
yield header.key, header.value
|
13
15
|
end
|
14
16
|
end
|
@@ -16,12 +18,11 @@ module RSpecApib
|
|
16
18
|
def keep_if
|
17
19
|
results = dup
|
18
20
|
results.content = []
|
19
|
-
content.select {|h| h.is_a?(Member)}.each do |header|
|
21
|
+
content.select { |h| h.is_a?(Member) }.each do |header|
|
20
22
|
results.content << header if yield header.key, header.value
|
21
23
|
end
|
22
24
|
results
|
23
25
|
end
|
24
|
-
|
25
26
|
end
|
26
27
|
end
|
27
28
|
end
|
@@ -1,9 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "json-schema"
|
2
3
|
require "rspec_apib/elements/http_message_payload"
|
3
4
|
module RSpecApib
|
4
5
|
module Element
|
6
|
+
# Represents a http message payload in api-elements (http://api-elements.readthedocs.io/en/latest/)
|
5
7
|
class HttpMessagePayload < Base
|
6
|
-
|
7
8
|
# The content type if defined else nil
|
8
9
|
# @return [String | NilClass] The content type header or nil
|
9
10
|
def content_type
|
@@ -31,7 +32,7 @@ module RSpecApib
|
|
31
32
|
reason: "Schema validation failure",
|
32
33
|
details: errors
|
33
34
|
}
|
34
|
-
|
35
|
+
[failure_reason]
|
35
36
|
end
|
36
37
|
|
37
38
|
private
|
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "json-schema"
|
2
3
|
require "rspec_apib/elements/http_message_payload"
|
3
4
|
module RSpecApib
|
4
5
|
module Element
|
6
|
+
# Represents a http request in api-elements (http://api-elements.readthedocs.io/en/latest/)
|
5
7
|
class HttpRequest < HttpMessagePayload
|
6
8
|
|
7
9
|
# Indicates if the incoming request matches the method, path and path vars
|
@@ -9,8 +11,8 @@ module RSpecApib
|
|
9
11
|
# @return [Boolean] true if matches else false
|
10
12
|
def matches?(request, options: {})
|
11
13
|
matches_method?(request) &&
|
12
|
-
|
13
|
-
|
14
|
+
matches_path?(request) &&
|
15
|
+
matches_headers?(request, options)
|
14
16
|
end
|
15
17
|
|
16
18
|
# Inherit href and hrefVariables from any ancestor
|
@@ -30,10 +32,16 @@ module RSpecApib
|
|
30
32
|
attributes && attributes["href"].to_s
|
31
33
|
end
|
32
34
|
|
35
|
+
def self.attributes_schema
|
36
|
+
{
|
37
|
+
href: "TemplatedHref"
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
33
41
|
private
|
34
42
|
|
35
|
-
def matches_headers?(request_or_response,
|
36
|
-
headers =
|
43
|
+
def matches_headers?(request_or_response, _options)
|
44
|
+
headers = compared_headers
|
37
45
|
return true if headers.nil?
|
38
46
|
headers.each_pair do |header_key, header_value|
|
39
47
|
return false unless request_or_response.headers.key?(header_key) &&
|
@@ -41,6 +49,10 @@ module RSpecApib
|
|
41
49
|
end
|
42
50
|
end
|
43
51
|
|
52
|
+
def compared_headers
|
53
|
+
attributes && attributes["headers"] && attributes["headers"].keep_if {|k, _v| k == "Content-Type" || k == "Accept"}
|
54
|
+
end
|
55
|
+
|
44
56
|
def matches_method?(request)
|
45
57
|
attributes && attributes["method"] && attributes["method"].downcase.to_sym == request.request_method
|
46
58
|
end
|
@@ -48,12 +60,6 @@ module RSpecApib
|
|
48
60
|
def matches_path?(request)
|
49
61
|
attributes && attributes["href"] && attributes["href"].matches_path?(request)
|
50
62
|
end
|
51
|
-
|
52
|
-
def self.attributes_schema
|
53
|
-
{
|
54
|
-
href: "TemplatedHref"
|
55
|
-
}
|
56
|
-
end
|
57
63
|
end
|
58
64
|
end
|
59
65
|
end
|
@@ -1,13 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "rspec_apib/elements/http_message_payload"
|
2
3
|
module RSpecApib
|
3
4
|
module Element
|
5
|
+
# Represents a http response in api-elements (http://api-elements.readthedocs.io/en/latest/)
|
4
6
|
class HttpResponse < HttpMessagePayload
|
5
7
|
# Indicates if the incoming request matches the method, path and path vars
|
6
8
|
# @param [::RSpecApib::Request] The incoming request - normalized
|
7
9
|
# @return [Boolean] true if matches else false
|
8
10
|
def matches?(response, options: {})
|
9
11
|
matches_status?(response) &&
|
10
|
-
|
12
|
+
matches_content_type?(response)
|
11
13
|
end
|
12
14
|
|
13
15
|
def status
|
@@ -1,15 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module RSpecApib
|
2
3
|
module Element
|
4
|
+
# Represents a http transaction in api-elements (http://api-elements.readthedocs.io/en/latest/)
|
3
5
|
class HttpTransaction < Base
|
4
|
-
|
5
6
|
def matches?(request_in, response_in, options: {})
|
6
7
|
request.matches?(request_in, options: options) && response.matches?(response_in, options: options)
|
7
8
|
end
|
8
9
|
|
9
10
|
def potential_match?(path:, request_method:, content_type:)
|
10
11
|
potential_match_content_type?(content_type) &&
|
11
|
-
|
12
|
-
|
12
|
+
(request_method == :any || request_method == request.request_method) &&
|
13
|
+
(path == :any || request.path == path)
|
13
14
|
end
|
14
15
|
|
15
16
|
def validate_schema(request_in, response_in, validate_request_schema: :always, validate_response_schema: :always)
|
@@ -1,11 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module RSpecApib
|
2
3
|
module Element
|
4
|
+
# Represents a member in api-elements (http://api-elements.readthedocs.io/en/latest/)
|
3
5
|
class Member < Base
|
4
|
-
|
5
6
|
def self.from_hash(hash, index:, parent:)
|
6
7
|
child = super
|
7
8
|
content = child.content
|
8
|
-
child.content = {content["key"] => content["value"]}
|
9
|
+
child.content = { content["key"] => content["value"] }
|
9
10
|
child
|
10
11
|
end
|
11
12
|
|
data/lib/rspec_apib/elements.rb
CHANGED
data/lib/rspec_apib/parser.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "rspec_apib/transcluder"
|
2
3
|
require "rspec_apib/extractors"
|
3
4
|
require "rspec_apib/elements"
|
@@ -65,7 +66,7 @@ module RSpecApib
|
|
65
66
|
|
66
67
|
def call_parser(file)
|
67
68
|
op = nil
|
68
|
-
Open3.popen3("#{bin_path} -f json") do |stdin, stdout,
|
69
|
+
Open3.popen3("#{bin_path} -f json") do |stdin, stdout, _stderr, wait_thr|
|
69
70
|
transcluder.each_line(file) do |line|
|
70
71
|
stdin.write line
|
71
72
|
end
|
data/lib/rspec_apib/request.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "forwardable"
|
1
3
|
module RSpecApib
|
2
4
|
class Request
|
5
|
+
extend Forwardable
|
3
6
|
def initialize(request)
|
4
7
|
self.raw_request = request
|
5
8
|
end
|
@@ -8,19 +11,15 @@ module RSpecApib
|
|
8
11
|
raw_request.method
|
9
12
|
end
|
10
13
|
|
11
|
-
|
12
|
-
raw_request.url
|
13
|
-
end
|
14
|
+
delegate url: :raw_request
|
14
15
|
|
15
16
|
def validate_body_with_json_schema?
|
16
|
-
request_method != :get &&
|
17
|
+
request_method != :get && json?
|
17
18
|
end
|
18
19
|
|
19
20
|
# The request body
|
20
21
|
# @return [String] The request body - always as a string
|
21
|
-
|
22
|
-
raw_request.body
|
23
|
-
end
|
22
|
+
delegate body: :raw_request
|
24
23
|
|
25
24
|
def content_type
|
26
25
|
headers["Content-Type"]
|
@@ -30,15 +29,13 @@ module RSpecApib
|
|
30
29
|
raw_request.request_headers
|
31
30
|
end
|
32
31
|
|
33
|
-
|
34
32
|
private
|
35
33
|
|
36
34
|
attr_accessor :raw_request
|
37
35
|
|
38
|
-
def
|
36
|
+
def json?
|
39
37
|
content_type =~ /json/
|
40
38
|
end
|
41
39
|
|
42
|
-
|
43
40
|
end
|
44
41
|
end
|