rspec_api_blueprint_matchers 0.1.4 → 0.1.5

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,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6dc5ebff2689d095904f17137d1554b9a3d05240
4
- data.tar.gz: bb68a6dcf5100596475dc17b36eb5e0e88f5ebe5
3
+ metadata.gz: c8bd0abe26c064df373492e78343f325abbd43c9
4
+ data.tar.gz: 7403ee224404cac77bc7706b11ed82e48285cb6b
5
5
  SHA512:
6
- metadata.gz: 6029d68ee28a95f469637a4385f2e5ff663d7452cf6e37e886117c9c0ea25208ebbcbe0dce3ebcfbc12bf08388c83d82b0f5d8b30a95bb078bd4268a85d571bb
7
- data.tar.gz: f596a4f15ecede8ac78cacd95ddae170364620fba52484f743912b90b3348e1ba7e4774753e7b8af2085049b88a3da24ffc7427262017ec6f6b5fb68ecb6919a
6
+ metadata.gz: f82510ff4c0051f2a614ba4df5dfbb77b2a28c97cc63b2d4e3574618d50aca81872f5de728289db9e31fcf0e304e62dba3227b6ad4073064d5242cde174484ed
7
+ data.tar.gz: 36f71401e0e26aae242359d060753a34c12f3afb46d9ca2eea935c5ce953db655c17339abbc0f0c9b770e8159c7757c659cfc54db9bcfe2dca28a6179d2ee2d6
@@ -21,6 +21,6 @@ AllCops:
21
21
  - "config/*"
22
22
  - "bin/*"
23
23
  - "config.ru"
24
- - "spec"
24
+ - "spec/**/*"
25
25
  - "vendor/gems/**/spec/**"
26
26
  TargetRubyVersion: 2.4
@@ -48,7 +48,7 @@ Metrics/CyclomaticComplexity:
48
48
  # Checks the length of lines in the source code.
49
49
 
50
50
  Metrics/LineLength:
51
- Max: 160
51
+ Max: 180
52
52
  AllowHereDoc: true
53
53
  AllowURI: true
54
54
  URISchemes:
@@ -80,7 +80,7 @@ Metrics/ModuleLength:
80
80
  # Checks for methods with too many parameters.
81
81
 
82
82
  Metrics/ParameterLists:
83
- Max: 5
83
+ Max: 6
84
84
  CountKeywordArgs: true
85
85
 
86
86
 
@@ -91,4 +91,3 @@ Metrics/ParameterLists:
91
91
 
92
92
  Metrics/PerceivedComplexity:
93
93
  Max: 7
94
-
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require "rspec_apib/version"
3
3
  require "rspec_apib/parser"
4
- require "rspec_apib/extractors"
5
4
  require "rspec_apib/transaction_validator"
6
5
  require "rspec_apib/rspec"
7
6
  require "rspec_apib/config"
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
  module RSpecApib
3
3
  module Element
4
+ # Represents an array in api-elements (http://api-elements.readthedocs.io/en/latest/)
4
5
  class Array < ::Array
6
+ # rubocop:disable Lint/UnusedMethodArgument
5
7
  def self.from_hash(hash, index:, parent:)
6
8
  new(hash["content"])
7
9
  end
@@ -19,10 +19,13 @@ module RSpecApib
19
19
  return node_or_nodes.map { |node| parse(node, index: index, parent: parent) } if node_or_nodes.is_a?(::Array)
20
20
  return transformed_basic_hash(node_or_nodes, index: index, parent: parent) if basic_hash?(node_or_nodes)
21
21
  return node_or_nodes unless !klass.nil? || base_element?(node_or_nodes)
22
- hash = node_or_nodes
22
+ parse_node(node_or_nodes, index: index, parent: parent, klass: klass)
23
+ end
24
+
25
+ def self.parse_node(hash, index:, parent:, klass: nil)
23
26
  klass_name = klass
24
27
  klass_name ||= hash["element"].slice(0, 1).capitalize + hash["element"].slice(1..-1).delete(" ")
25
- return node_or_nodes unless RSpecApib::Element.const_defined?(klass_name)
28
+ return hash unless RSpecApib::Element.const_defined?(klass_name)
26
29
  klass = RSpecApib::Element.const_get(klass_name)
27
30
  index[klass] ||= []
28
31
  element = klass.from_hash(hash, index: index, parent: parent)
@@ -23,6 +23,12 @@ module RSpecApib
23
23
  }
24
24
  return [failure_reason]
25
25
  end
26
+ validate_json_schema(schema, request_or_response)
27
+ end
28
+
29
+ private
30
+
31
+ def validate_json_schema(schema, request_or_response)
26
32
  schema = JSON.parse schema.content
27
33
  errors = JSON::Validator.fully_validate(schema, request_or_response.body)
28
34
  return [] if errors.length.zero?
@@ -35,8 +41,6 @@ module RSpecApib
35
41
  [failure_reason]
36
42
  end
37
43
 
38
- private
39
-
40
44
  def body_schema_asset
41
45
  content.find { |node| node.is_a?(Asset) && node.meta && node.meta["classes"] && node.meta["classes"].include?("messageBodySchema")}
42
46
  end
@@ -7,6 +7,7 @@ module RSpecApib
7
7
  # Indicates if the incoming request matches the method, path and path vars
8
8
  # @param [::RSpecApib::Request] The incoming request - normalized
9
9
  # @return [Boolean] true if matches else false
10
+ # rubocop:disable Lint/UnusedMethodArgument
10
11
  def matches?(response, options: {})
11
12
  matches_status?(response) &&
12
13
  matches_content_type?(response)
@@ -1,9 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module RSpecApib
3
3
  module Element
4
- # (byebug) parent.parent["attributes"]
5
- # {"meta"=>[#<struct RSpecApib::Element::Member element="member", meta={"classes"=>["user"]}, attributes={}, content={"FORMAT"=>"1A"}, parent=#<struct RSpecApib::Element::ParseResult element="parseResult", meta=nil, attributes=nil, content=nil, parent=nil>>, #<struct RSpecApib::Element::Member element="member", meta={"classes"=>["user"]}, attributes={}, content={"HOST"=>"http://api.shiftcommerce.com/inventory/v1"}, parent=#<struct RSpecApib::Element::ParseResult element="parseResult", meta=nil, attributes=nil, content=nil, parent=nil>>]}
6
-
4
+ # Represents a resource in api-elements (http://api-elements.readthedocs.io/en/latest/)
7
5
  class Resource < Base
8
6
  def transitions
9
7
  content.select { |item| item.is_a?(Transition) }
@@ -13,8 +11,6 @@ module RSpecApib
13
11
  content.select { |item| item.is_a?(Category) }
14
12
  end
15
13
 
16
- private
17
-
18
14
  def self.attributes_schema
19
15
  {
20
16
  href: "TemplatedHref"
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
  module RSpecApib
3
3
  module Element
4
+ # Represents a string in api-elements (http://api-elements.readthedocs.io/en/latest/)
4
5
  class String < ::String
6
+ # rubocop:disable Lint/UnusedMethodArgument
5
7
  def self.from_hash(hash, index:, parent:)
6
8
  new(hash["content"] || "")
7
9
  end
@@ -2,8 +2,11 @@
2
2
  require "addressable"
3
3
  module RSpecApib
4
4
  module Element
5
+ # Represents a templated href in api-elements (http://api-elements.readthedocs.io/en/latest/)
5
6
  class TemplatedHref < Base
6
- # Note this is not really a hash !!
7
+ # Note this is not really a hash but the interface named it
8
+ # that early on as everything was a hash !!
9
+ # rubocop:disable Lint/UnusedMethodArgument
7
10
  def self.from_hash(hash, index:, parent:)
8
11
  new("templatedHref", nil, nil, hash, parent)
9
12
  end
@@ -23,10 +26,10 @@ module RSpecApib
23
26
  end
24
27
 
25
28
  def path
26
- a1 = Addressable::URI.parse(url)
27
- a1.path, a1.query, a1.fragment = nil
28
- a2 = Addressable::URI.parse(url)
29
- a2.to_s.gsub(a1.to_s, "")
29
+ url_1 = Addressable::URI.parse(url)
30
+ url_1.path, url_1.query, url_1.fragment = nil
31
+ url_2 = Addressable::URI.parse(url)
32
+ url_2.to_s.gsub(url_1.to_s, "")
30
33
  end
31
34
 
32
35
  def self.host_from_parent(node)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module RSpecApib
3
3
  module Element
4
+ # Represents a transition in api-elements (http://api-elements.readthedocs.io/en/latest/)
4
5
  class Transition < Base
5
6
  def http_transactions
6
7
  content.select { |item| item.is_a?(HttpTransaction) }
@@ -11,8 +12,6 @@ module RSpecApib
11
12
  [:href, :hrefVariables]
12
13
  end
13
14
 
14
- private
15
-
16
15
  def self.attributes_schema
17
16
  {
18
17
  href: "TemplatedHref"
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require "rspec_apib/transcluder"
3
- require "rspec_apib/extractors"
4
3
  require "rspec_apib/elements"
5
4
  require "open3"
6
5
  require "json"
@@ -73,7 +72,7 @@ module RSpecApib
73
72
  Open3.popen3("#{bin_path} -f json") do |stdin, stdout, _stderr, wait_thr|
74
73
  send_document(file: file, buffer: stdin)
75
74
  op = stdout.read
76
- exit_status = wait_thr.value
75
+ wait_thr.value
77
76
  end
78
77
  op
79
78
  end
@@ -17,7 +17,7 @@ module RSpecApib
17
17
  end
18
18
 
19
19
  def validate_body_with_json_schema?
20
- is_json?
20
+ json?
21
21
  end
22
22
 
23
23
  def content_type
@@ -32,7 +32,7 @@ module RSpecApib
32
32
 
33
33
  attr_accessor :raw_response
34
34
 
35
- def is_json?
35
+ def json?
36
36
  content_type =~ /json/
37
37
  end
38
38
  end
@@ -12,7 +12,8 @@ RSpec::Matchers.define :match_api_docs_for do |path:, request_method:, content_t
12
12
  error_messages << "Expected the transaction to match api docs for #{method}:#{path} but the provided parser was invalid"
13
13
  false
14
14
  else
15
- ::RSpecApib::TransactionValidator.new(path: path, request_method: request_method, content_type: content_type, parser: parser).validate(request: ::RSpecApib.normalize_request(actual.request), response: ::RSpecApib.normalize_response(actual.response), error_messages: error_messages)
15
+ validator = ::RSpecApib::TransactionValidator.new(path: path, request_method: request_method, content_type: content_type, parser: parser)
16
+ validator.validate(request: ::RSpecApib.normalize_request(actual.request), response: ::RSpecApib.normalize_response(actual.response), error_messages: error_messages)
16
17
  end
17
18
 
18
19
  end
@@ -12,7 +12,7 @@ module RSpecApib
12
12
  acc
13
13
  end
14
14
  transactions.each do |requested_tx|
15
- matching_transactions = documented_transaction_tracker.keys.each do |dtx|
15
+ documented_transaction_tracker.keys.each do |dtx|
16
16
  documented_transaction_tracker[dtx] = true if dtx.matches?(requested_tx.request, requested_tx.response)
17
17
  end
18
18
  end
@@ -30,8 +30,29 @@ module RSpecApib
30
30
  results
31
31
  end
32
32
 
33
+ def report_uncovered(errors:)
34
+ uncovered_transactions.each do |tx|
35
+ errors << self.class.uncovered_tx_message(tx)
36
+ end
37
+ end
38
+
39
+ def report_undocumented(errors:)
40
+ undocumented.each do |tx|
41
+ errors << self.class.undocumented_tx_message(tx)
42
+ end
43
+ end
44
+
33
45
  private
34
46
 
35
47
  attr_accessor :transactions, :parser
48
+
49
+ def self.uncovered_tx_message(tx)
50
+ "#{tx.request.request_method.to_s.upcase} #{tx.request.url} with response (#{tx.response.content_type}) status #{tx.response.status}- Not covered"
51
+ end
52
+
53
+ def self.undocumented_tx_message(tx)
54
+ "#{tx.request.request_method.upcase} #{tx.request.url} with response (#{tx.response.content_type}) status #{tx.response.status} - Not documented"
55
+ end
56
+
36
57
  end
37
58
  end
@@ -9,18 +9,14 @@ module RSpecApib
9
9
  def validate(transactions:, error_messages:)
10
10
  errors = []
11
11
  reporter = TransactionCoverageReport.new(transactions: transactions, parser: parser)
12
- uncovered = reporter.uncovered_transactions
13
- unless uncovered.empty?
14
- uncovered.each do |tx|
15
- errors << self.class.uncovered_tx_message(tx)
16
- end
17
- end
18
- undocumented = reporter.undocumented_transactions
19
- unless undocumented.empty?
20
- undocumented.each do |tx|
21
- errors << self.class.undocumented_tx_message(tx)
22
- end
23
- end
12
+ reporter.report_uncovered(errors: errors)
13
+ reporter.report_undocumented(errors: errors)
14
+ report_summary(errors: errors, error_messages: error_messages)
15
+ end
16
+
17
+ private
18
+
19
+ def report_summary(errors:, error_messages:)
24
20
  if !errors.empty?
25
21
  error_messages.concat errors
26
22
  error_messages << "Coverage Summary: #{uncovered.length} uncovered and #{undocumented.length} undocumented transactions"
@@ -30,21 +26,10 @@ module RSpecApib
30
26
  end
31
27
  end
32
28
 
33
- private
34
-
35
29
  attr_accessor :parser
36
30
 
37
31
  def matched_transaction(request:, response:)
38
32
  parser.http_transactions.find { |t| t.matches?(request, response, options: {validate_request_schema: :never, validate_response_schema: :never}) }
39
33
  end
40
-
41
- def self.uncovered_tx_message(tx)
42
- "#{tx.request.request_method.to_s.upcase} #{tx.request.url} with response (#{tx.response.content_type}) status #{tx.response.status}- Not covered"
43
- end
44
-
45
- def self.undocumented_tx_message(tx)
46
- "#{tx.request.request_method.upcase} #{tx.request.url} with response (#{tx.response.content_type}) status #{tx.response.status} - Not documented"
47
- end
48
-
49
34
  end
50
35
  end
@@ -12,25 +12,32 @@ module RSpecApib
12
12
  end
13
13
 
14
14
  def validate(request:, response:, error_messages:, options: {})
15
- candidates = transaction_candidates(request: request, response: response, options: options)
16
- results = candidates.map do |candidate|
17
- candidate.validate_schema(request, response, validate_request_schema: validate_request_schema, validate_response_schema: validate_response_schema)
18
- end
19
-
15
+ results = matched_transactions(request, response, options)
20
16
  if results.empty?
21
17
  error_messages << "No candidates for #{request.inspect} with response #{response.inspect}"
22
18
  return false
23
19
  end
24
20
 
25
21
  return true unless results.flatten.find {|r| !r[:request_errors].empty? || !r[:response_errors].empty?}
22
+ report_error_messages(results, error_messages)
23
+ false
24
+ end
25
+
26
+ private
27
+
28
+ def report_error_messages(results, error_messages)
26
29
  results.each do |result|
27
30
  error_messages << "The request validation failed - reasons #{result[:request_errors].join("\n")}" unless result[:request_errors].empty?
28
31
  error_messages << "The response validation failed - reasons #{result[:response_errors].join("\n")}" unless result[:response_errors].empty?
29
32
  end
30
- false
31
33
  end
32
34
 
33
- private
35
+ def matched_transactions(request, response, options)
36
+ candidates = transaction_candidates(request: request, response: response, options: options)
37
+ candidates.map do |candidate|
38
+ candidate.validate_schema(request, response, validate_request_schema: validate_request_schema, validate_response_schema: validate_response_schema)
39
+ end
40
+ end
34
41
 
35
42
  def transaction_candidates(request:, response:, options: {})
36
43
  transactions = parser.http_transactions.select do |t|
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module RSpecApib
3
- VERSION = "0.1.4".freeze
3
+ VERSION = "0.1.5".freeze
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec_api_blueprint_matchers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shift Commerce Ltd
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-05-08 00:00:00.000000000 Z
11
+ date: 2017-05-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -163,9 +163,6 @@ files:
163
163
  - lib/rspec_apib/elements/string.rb
164
164
  - lib/rspec_apib/elements/templated_href.rb
165
165
  - lib/rspec_apib/elements/transition.rb
166
- - lib/rspec_apib/extractors.rb
167
- - lib/rspec_apib/extractors/http_transaction.rb
168
- - lib/rspec_apib/extractors/resource.rb
169
166
  - lib/rspec_apib/parser.rb
170
167
  - lib/rspec_apib/request.rb
171
168
  - lib/rspec_apib/response.rb
@@ -1,3 +0,0 @@
1
- # frozen_string_literal: true
2
- require "rspec_apib/extractors/resource"
3
- require "rspec_apib/extractors/http_transaction"
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
- module RSpecApib
3
- module Extractor
4
- class HttpTransaction
5
- def self.call(document)
6
- collector = []
7
- find_nodes_within(document, collector: collector)
8
- collector
9
- end
10
-
11
- private
12
-
13
- def self.find_nodes_within(node, collector:)
14
- return node.each { |node| find_nodes_within(node, collector: collector)} if node.is_a?(Array)
15
- return unless node.is_a?(Hash)
16
- if node["element"] == "httpTransaction"
17
- collector << node
18
- elsif node.key?("content")
19
- find_nodes_within(node["content"], collector: collector)
20
- end
21
- end
22
- end
23
- end
24
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
- module RSpecApib
3
- module Extractor
4
- class Resource
5
- def self.call(document)
6
- collector = []
7
- find_nodes_within(document, collector: collector)
8
- collector
9
- end
10
-
11
- private
12
-
13
- def self.find_nodes_within(node, collector:)
14
- return node.each { |node| find_nodes_within(node, collector: collector)} if node.is_a?(Array)
15
- return unless node.is_a?(Hash)
16
- if node["element"] == "resource"
17
- collector << node
18
- elsif node.key?("content")
19
- find_nodes_within(node["content"], collector: collector)
20
- end
21
- end
22
- end
23
- end
24
- end