soaspec 0.2.32 → 0.2.33

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +15 -15
  3. data/.gitlab-ci.yml +62 -62
  4. data/.rspec +3 -3
  5. data/.rubocop.yml +2 -2
  6. data/CODE_OF_CONDUCT.md +74 -74
  7. data/ChangeLog +632 -625
  8. data/Dockerfile +7 -7
  9. data/Gemfile +8 -8
  10. data/LICENSE.txt +21 -21
  11. data/README.md +231 -231
  12. data/Rakefile +52 -52
  13. data/Todo.md +16 -16
  14. data/exe/soaspec +138 -138
  15. data/exe/xml_to_yaml_file +43 -43
  16. data/lib/soaspec.rb +106 -105
  17. data/lib/soaspec/baseline.rb +23 -0
  18. data/lib/soaspec/core_ext/hash.rb +44 -44
  19. data/lib/soaspec/cucumber/generic_steps.rb +94 -94
  20. data/lib/soaspec/demo.rb +6 -6
  21. data/lib/soaspec/errors.rb +24 -22
  22. data/lib/soaspec/exchange/exchange.rb +129 -129
  23. data/lib/soaspec/exchange/exchange_extractor.rb +90 -90
  24. data/lib/soaspec/exchange/exchange_properties.rb +28 -28
  25. data/lib/soaspec/exchange/exchange_repeater.rb +21 -21
  26. data/lib/soaspec/exchange/request_builder.rb +70 -70
  27. data/lib/soaspec/exchange/variable_storer.rb +24 -24
  28. data/lib/soaspec/exchange_handlers/exchange_handler.rb +98 -98
  29. data/lib/soaspec/exchange_handlers/exchange_handler_defaults.rb +61 -61
  30. data/lib/soaspec/exchange_handlers/handler_accessors.rb +132 -132
  31. data/lib/soaspec/exchange_handlers/request/rest_request.rb +59 -51
  32. data/lib/soaspec/exchange_handlers/request/soap_request.rb +41 -41
  33. data/lib/soaspec/exchange_handlers/response_extractor.rb +84 -84
  34. data/lib/soaspec/exchange_handlers/rest_exchanger_factory.rb +111 -111
  35. data/lib/soaspec/exchange_handlers/rest_handler.rb +307 -298
  36. data/lib/soaspec/exchange_handlers/rest_methods.rb +65 -65
  37. data/lib/soaspec/exchange_handlers/rest_parameters.rb +112 -112
  38. data/lib/soaspec/exchange_handlers/rest_parameters_defaults.rb +42 -42
  39. data/lib/soaspec/exchange_handlers/soap_handler.rb +241 -241
  40. data/lib/soaspec/exe_helpers.rb +94 -94
  41. data/lib/soaspec/generate_server.rb +48 -48
  42. data/lib/soaspec/generator/.rspec.erb +5 -5
  43. data/lib/soaspec/generator/.travis.yml.erb +5 -5
  44. data/lib/soaspec/generator/Gemfile.erb +8 -8
  45. data/lib/soaspec/generator/README.md.erb +29 -29
  46. data/lib/soaspec/generator/Rakefile.erb +19 -19
  47. data/lib/soaspec/generator/config/data/default.yml.erb +2 -2
  48. data/lib/soaspec/generator/css/bootstrap.css +6833 -6833
  49. data/lib/soaspec/generator/features/support/env.rb.erb +3 -3
  50. data/lib/soaspec/generator/generate_exchange.html.erb +47 -47
  51. data/lib/soaspec/generator/lib/blz_service.rb.erb +26 -26
  52. data/lib/soaspec/generator/lib/dynamic_class_content.rb.erb +12 -12
  53. data/lib/soaspec/generator/lib/new_rest_service.rb.erb +56 -56
  54. data/lib/soaspec/generator/lib/new_soap_service.rb.erb +29 -29
  55. data/lib/soaspec/generator/lib/package_service.rb.erb +2 -2
  56. data/lib/soaspec/generator/lib/shared_example.rb.erb +8 -8
  57. data/lib/soaspec/generator/spec/dynamic_soap_spec.rb.erb +12 -12
  58. data/lib/soaspec/generator/spec/rest_spec.rb.erb +9 -9
  59. data/lib/soaspec/generator/spec/soap_spec.rb.erb +51 -51
  60. data/lib/soaspec/generator/spec/spec_helper.rb.erb +23 -23
  61. data/lib/soaspec/generator/template/soap_template.xml +6 -6
  62. data/lib/soaspec/indifferent_hash.rb +9 -9
  63. data/lib/soaspec/interpreter.rb +70 -70
  64. data/lib/soaspec/matchers.rb +140 -118
  65. data/lib/soaspec/o_auth2.rb +142 -142
  66. data/lib/soaspec/soaspec_shared_examples.rb +26 -26
  67. data/lib/soaspec/spec_logger.rb +143 -143
  68. data/lib/soaspec/template_reader.rb +30 -30
  69. data/lib/soaspec/test_server/bank.wsdl +90 -90
  70. data/lib/soaspec/test_server/get_bank.rb +166 -166
  71. data/lib/soaspec/test_server/id_manager.rb +41 -41
  72. data/lib/soaspec/test_server/invoices.rb +29 -29
  73. data/lib/soaspec/test_server/namespace.xml +14 -14
  74. data/lib/soaspec/test_server/note.xml +5 -5
  75. data/lib/soaspec/test_server/puppy_service.rb +21 -21
  76. data/lib/soaspec/test_server/test_attribute.rb +14 -14
  77. data/lib/soaspec/test_server/test_namespace.rb +14 -14
  78. data/lib/soaspec/version.rb +6 -6
  79. data/lib/soaspec/virtual_server.rb +190 -176
  80. data/lib/soaspec/wait.rb +43 -43
  81. data/lib/soaspec/wsdl_generator.rb +215 -215
  82. data/soaspec.gemspec +58 -58
  83. data/test.wsdl +116 -116
  84. data/test.xml +10 -10
  85. data/test_wsdl.rb +43 -43
  86. metadata +4 -3
@@ -1,23 +1,23 @@
1
-
2
- require 'soaspec'
3
- require 'require_all'
4
- require_all 'lib'
5
- require 'data_magic'
6
-
7
- include DataMagic # Used as example of loading data smartly. Use 'data_for' method to load yml data
8
- <% if @type == 'rest' %>
9
- include Soaspec::RestMethods
10
- <% end %>
11
-
12
- RSpec.configure do |config|
13
- # This will make backtrace much shorter by removing many lines from rspec failure message
14
- config.backtrace_exclusion_patterns = [
15
- /rspec/
16
- ]
17
- <% if @virtual %>
18
- # Close test server after all RSpec tests have run
19
- config.after(:suite) do
20
- Process.kill(:QUIT, ENV['test_server_pid'].to_i) if ENV['test_server_pid'] && !ENV['leave_server_running']
21
- end
22
- <% end %>
23
- end
1
+
2
+ require 'soaspec'
3
+ require 'require_all'
4
+ require_all 'lib'
5
+ require 'data_magic'
6
+
7
+ include DataMagic # Used as example of loading data smartly. Use 'data_for' method to load yml data
8
+ <% if @type == 'rest' %>
9
+ include Soaspec::RestMethods
10
+ <% end %>
11
+
12
+ RSpec.configure do |config|
13
+ # This will make backtrace much shorter by removing many lines from rspec failure message
14
+ config.backtrace_exclusion_patterns = [
15
+ /rspec/
16
+ ]
17
+ <% if @virtual %>
18
+ # Close test server after all RSpec tests have run
19
+ config.after(:suite) do
20
+ Process.kill(:QUIT, ENV['test_server_pid'].to_i) if ENV['test_server_pid'] && !ENV['leave_server_running']
21
+ end
22
+ <% end %>
23
+ end
@@ -1,7 +1,7 @@
1
- <env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://thomas-bayer.com/blz/" xmlns:env="http://www.w3.org/2003/05/soap-envelope">
2
- <env:Body>
3
- <tns:getBank>
4
- <tns:blz><%= test_values[:blz] || '70070010' %></tns:blz>
5
- </tns:getBank>
6
- </env:Body>
1
+ <env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://thomas-bayer.com/blz/" xmlns:env="http://www.w3.org/2003/05/soap-envelope">
2
+ <env:Body>
3
+ <tns:getBank>
4
+ <tns:blz><%= test_values[:blz] || '70070010' %></tns:blz>
5
+ </tns:getBank>
6
+ </env:Body>
7
7
  </env:Envelope>
@@ -1,9 +1,9 @@
1
- # frozen_string_literal: true
2
-
3
- require 'hashie'
4
-
5
- # Hash that allows accessing hash with either string or Hash
6
- class IndifferentHash < Hash
7
- include Hashie::Extensions::MergeInitializer
8
- include Hashie::Extensions::IndifferentAccess
9
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'hashie'
4
+
5
+ # Hash that allows accessing hash with either string or Hash
6
+ class IndifferentHash < Hash
7
+ include Hashie::Extensions::MergeInitializer
8
+ include Hashie::Extensions::IndifferentAccess
9
+ end
@@ -1,70 +1,70 @@
1
- # frozen_string_literal: true
2
-
3
- # Help interpret the general type of a particular object
4
- class Interpreter
5
- class << self
6
- # @return [Error] XML Errors found in interpreting response
7
- attr_accessor :xml_errors
8
-
9
- # @return [Error] JSON Errors found in interpreting response
10
- attr_accessor :json_errors
11
-
12
- # @param [Object] response API response
13
- # @return [Symbol] Type of provided response
14
- def response_type_for(response)
15
- @xml_errors = nil
16
- @json_errors = nil
17
- @response = response
18
- if @response.is_a? String
19
- if xml?
20
- :xml
21
- elsif json?
22
- :json
23
- else
24
- :string
25
- end
26
- elsif response.is_a? Hash
27
- :hash
28
- elsif response.is_a?(Nokogiri::XML::NodeSet) || response.is_a?(Nokogiri::XML::Document)
29
- :xml
30
- else
31
- :unknown
32
- end
33
- end
34
-
35
- # @return [Boolean] Whether response has tag like syntax similar to XML. Could be a syntax error occurred
36
- def looks_like_xml?
37
- @response[0] == '<' && @response[-1] == '>'
38
- end
39
-
40
- # @return [Boolean] Whether response has bracket like syntax similar to JSON. Could be a syntax error occurred
41
- def looks_like_json?
42
- @response[0] == '{' && @response[-1] == '}'
43
- end
44
-
45
- # @return [String] Description of error
46
- def diagnose_error
47
- return xml_errors if looks_like_xml?
48
-
49
- return json_errors if looks_like_json?
50
-
51
- ''
52
- end
53
-
54
- # @return [Boolean] Whether valid XML
55
- def xml?
56
- Nokogiri::XML(@response) { |config| config.options = Nokogiri::XML::ParseOptions::STRICT }
57
- rescue Nokogiri::XML::SyntaxError => e
58
- self.xml_errors = e
59
- false
60
- end
61
-
62
- # @return [Boolean] Whether valid JSON
63
- def json?
64
- JSON.parse(@response)
65
- rescue JSON::ParserError => e
66
- self.json_errors = e
67
- false
68
- end
69
- end
70
- end
1
+ # frozen_string_literal: true
2
+
3
+ # Help interpret the general type of a particular object
4
+ class Interpreter
5
+ class << self
6
+ # @return [Error] XML Errors found in interpreting response
7
+ attr_accessor :xml_errors
8
+
9
+ # @return [Error] JSON Errors found in interpreting response
10
+ attr_accessor :json_errors
11
+
12
+ # @param [Object] response API response
13
+ # @return [Symbol] Type of provided response
14
+ def response_type_for(response)
15
+ @xml_errors = nil
16
+ @json_errors = nil
17
+ @response = response
18
+ if @response.is_a? String
19
+ if xml?
20
+ :xml
21
+ elsif json?
22
+ :json
23
+ else
24
+ :string
25
+ end
26
+ elsif response.is_a? Hash
27
+ :hash
28
+ elsif response.is_a?(Nokogiri::XML::NodeSet) || response.is_a?(Nokogiri::XML::Document)
29
+ :xml
30
+ else
31
+ :unknown
32
+ end
33
+ end
34
+
35
+ # @return [Boolean] Whether response has tag like syntax similar to XML. Could be a syntax error occurred
36
+ def looks_like_xml?
37
+ @response[0] == '<' && @response[-1] == '>'
38
+ end
39
+
40
+ # @return [Boolean] Whether response has bracket like syntax similar to JSON. Could be a syntax error occurred
41
+ def looks_like_json?
42
+ @response[0] == '{' && @response[-1] == '}'
43
+ end
44
+
45
+ # @return [String] Description of error
46
+ def diagnose_error
47
+ return xml_errors if looks_like_xml?
48
+
49
+ return json_errors if looks_like_json?
50
+
51
+ ''
52
+ end
53
+
54
+ # @return [Boolean] Whether valid XML
55
+ def xml?
56
+ Nokogiri::XML(@response) { |config| config.options = Nokogiri::XML::ParseOptions::STRICT }
57
+ rescue Nokogiri::XML::SyntaxError => e
58
+ self.xml_errors = e
59
+ false
60
+ end
61
+
62
+ # @return [Boolean] Whether valid JSON
63
+ def json?
64
+ JSON.parse(@response)
65
+ rescue JSON::ParserError => e
66
+ self.json_errors = e
67
+ false
68
+ end
69
+ end
70
+ end
@@ -1,118 +1,140 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'core_ext/hash'
4
- require_relative 'errors'
5
-
6
- # Whether response has any element with the provided value
7
- RSpec::Matchers.define :contain_value do |expected|
8
- match do |actual|
9
- expect(actual.exchange_handler.include_value?(actual.response, expected)).to be true
10
- end
11
-
12
- failure_message do |actual|
13
- "expected that #{actual.exchange_handler.response_body(actual.response, format: :hash)} would contain value #{expected}"
14
- end
15
- end
16
-
17
- # Whether substring exists in body of response (more general than above)
18
- RSpec::Matchers.define :include_in_body do |expected|
19
- match do |actual|
20
- expect(actual.exchange_handler.include_in_body?(actual.response, expected)).to be true
21
- end
22
-
23
- failure_message do |actual|
24
- "expected that #{actual.exchange_handler.response_body(actual.response, format: :raw)} would contain value #{expected}"
25
- end
26
- end
27
-
28
- # Whether an element exists at expected xpath
29
- RSpec::Matchers.define :have_element_at_path do |xpath|
30
- match do |object|
31
- # Object like `response` returns the Exchange object from which a path can be obtained
32
- exchange = object.respond_to?(:exchange) ? object.exchange : object
33
- expect { exchange[xpath] }.to_not raise_error # Error will be raised if Path returns no value
34
- end
35
-
36
- # TODO: Would be better to print failure message
37
- failure_message do |object|
38
- # Object like `response` returns the Exchange object from which a path can be obtained
39
- exchange = object.respond_to?(:exchange) ? object.exchange : object
40
- "expected that #{exchange.exchange_handler.response_body(exchange.response, format: :raw)} would have element at path '#{xpath}'"
41
- end
42
- end
43
-
44
- RSpec::Matchers.alias_matcher :have_element_at_xpath, :have_element_at_path
45
- RSpec::Matchers.alias_matcher :contain_key, :have_element_at_path
46
-
47
- # Whether an element at xpath (defined by key) has value (defined by value).
48
- # @param [Hash] expected_hash Xpath => Value pair (e.g. '//xmlns:GetWeatherResult' => 'Data Not Found')
49
- RSpec::Matchers.define :have_xpath_value do |expected_hash|
50
- match do |object|
51
- # Object like `response` returns the Exchange object from which a path can be obtained
52
- exchange = object.respond_to?(:exchange) ? object.exchange : object
53
- expected_hash = Hash[*expected_hash.flatten] if expected_hash.is_a?(Array) # For some reason Array was occuring
54
- expect(exchange[expected_hash.keys.first]).to eq expected_hash.values.first
55
- end
56
-
57
- failure_message do |actual|
58
- "expected that xpath '#{expected_hash.keys.first}' has value '#{expected_hash.values.first}' but was '#{actual[expected_hash.keys.first]}'"
59
- end
60
- end
61
-
62
- RSpec::Matchers.alias_matcher :have_jsonpath_value, :have_xpath_value
63
-
64
- RSpec::Matchers.define :be_found do
65
- match do |exchange|
66
- expect(exchange.exchange_handler.found?(exchange.response)).to be true
67
- end
68
-
69
- failure_message do |exchange|
70
- "expected result #{exchange.response} to be found. Status code is #{exchange.response.code}"
71
- end
72
- end
73
-
74
- # Whether response has successful status code and correct mandatory elements and values
75
- RSpec::Matchers.define :be_successful do
76
- # @param [Exchange, RestClient::Response] actual Object that returns Exchange or is Exchange
77
- # @return [Exchange] Exchange to use
78
- def exchange_from(actual)
79
- actual.respond_to?(:exchange) ? actual.exchange : actual
80
- end
81
-
82
- # @param [Exchange, RestClient::Response] exchange Object that returns Exchange or is Exchange
83
- # @return [Array] List of errors when checking Exchange response is successful
84
- def collect_errors(exchange)
85
- failure_list = []
86
- failure_list << "#{exchange.status_code} not valid status code" unless exchange.successful_status_code?
87
- exchange.exchange_handler.expected_mandatory_elements.each do |mandatory_element_path|
88
- begin
89
- exchange[mandatory_element_path]
90
- rescue NoElementAtPath => e
91
- failure_list << e.message
92
- end
93
- end
94
- exchange.exchange_handler.expected_mandatory_xpath_values.each do |path, value|
95
- failure_list << "Expected value at xpath '#{path}' to be '#{value}' but was '#{exchange[path]}'" unless exchange[path] == value
96
- end
97
- exchange.exchange_handler.expected_mandatory_json_values.each do |path, value|
98
- failure_list << "Expected value at json '#{path}' to be '#{value}' but was '#{exchange[path]}'" unless exchange[path] == value
99
- end
100
- failure_list
101
- end
102
-
103
- match do |actual|
104
- exchange = exchange_from actual
105
- failure_list = collect_errors exchange
106
- raise failure_list.to_s unless failure_list.empty?
107
-
108
- true
109
- end
110
-
111
- match_when_negated do |actual|
112
- exchange = exchange_from actual
113
- failure_list = collect_errors exchange
114
- raise "Expected failure. Status code is #{exchange.status_code}" if failure_list.empty?
115
-
116
- true
117
- end
118
- end
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'core_ext/hash'
4
+ require_relative 'errors'
5
+
6
+ # Whether response has any element with the provided value
7
+ RSpec::Matchers.define :contain_value do |expected|
8
+ match do |actual|
9
+ expect(actual.exchange_handler.include_value?(actual.response, expected)).to be true
10
+ end
11
+
12
+ failure_message do |actual|
13
+ "expected that #{actual.exchange_handler.response_body(actual.response, format: :hash)} would contain value #{expected}"
14
+ end
15
+ end
16
+
17
+ # Whether substring exists in body of response (more general than above)
18
+ RSpec::Matchers.define :include_in_body do |expected|
19
+ match do |actual|
20
+ expect(actual.exchange_handler.include_in_body?(actual.response, expected)).to be true
21
+ end
22
+
23
+ failure_message do |actual|
24
+ "expected that #{actual.exchange_handler.response_body(actual.response, format: :raw)} would contain value #{expected}"
25
+ end
26
+ end
27
+
28
+ # Whether an element exists at expected xpath
29
+ RSpec::Matchers.define :have_element_at_path do |xpath|
30
+ match do |object|
31
+ # Object like `response` returns the Exchange object from which a path can be obtained
32
+ exchange = object.respond_to?(:exchange) ? object.exchange : object
33
+ expect { exchange[xpath] }.to_not raise_error # Error will be raised if Path returns no value
34
+ end
35
+
36
+ # TODO: Would be better to print failure message
37
+ failure_message do |object|
38
+ # Object like `response` returns the Exchange object from which a path can be obtained
39
+ exchange = object.respond_to?(:exchange) ? object.exchange : object
40
+ "expected that #{exchange.exchange_handler.response_body(exchange.response, format: :raw)} would have element at path '#{xpath}'"
41
+ end
42
+ end
43
+
44
+ RSpec::Matchers.alias_matcher :have_element_at_xpath, :have_element_at_path
45
+ RSpec::Matchers.alias_matcher :contain_key, :have_element_at_path
46
+
47
+ # Whether an element at xpath (defined by key) has value (defined by value).
48
+ # @param [Hash] expected_hash Xpath => Value pair (e.g. '//xmlns:GetWeatherResult' => 'Data Not Found')
49
+ RSpec::Matchers.define :have_xpath_value do |expected_hash|
50
+ match do |object|
51
+ # Object like `response` returns the Exchange object from which a path can be obtained
52
+ exchange = object.respond_to?(:exchange) ? object.exchange : object
53
+ expected_hash = Hash[*expected_hash.flatten] if expected_hash.is_a?(Array) # For some reason Array was occuring
54
+ expect(exchange[expected_hash.keys.first]).to eq expected_hash.values.first
55
+ end
56
+
57
+ failure_message do |actual|
58
+ "expected that xpath '#{expected_hash.keys.first}' has value '#{expected_hash.values.first}' but was '#{actual[expected_hash.keys.first]}'"
59
+ end
60
+ end
61
+
62
+ RSpec::Matchers.alias_matcher :have_jsonpath_value, :have_xpath_value
63
+
64
+ RSpec::Matchers.define :be_found do
65
+ match do |exchange|
66
+ expect(exchange.exchange_handler.found?(exchange.response)).to be true
67
+ end
68
+
69
+ failure_message do |exchange|
70
+ "expected result #{exchange.response} to be found. Status code is #{exchange.response.code}"
71
+ end
72
+ end
73
+
74
+ RSpec::Matchers.define :match_baseline do
75
+
76
+ match do |exchange|
77
+ file = Soaspec::Baseline.file(exchange)
78
+ if File.exist?(file)
79
+ exchange.to_hash == YAML.load_file(file)
80
+ else
81
+ Soaspec::Baseline.create_file filename: file,
82
+ content: Psych.dump(exchange.to_hash)
83
+ raise Soaspec::BaselineError,
84
+ "Created baseline at #{file}. Inspect file to ensure it is
85
+ correct and rerun to ensure baseline is stable"
86
+ end
87
+ end
88
+
89
+ failure_message do |exchange|
90
+ "#{exchange} did not match baseline. \n" \
91
+ "Expected: #{YAML.load_file(Soaspec::Baseline.file(exchange))} \n" \
92
+ " Actual: #{exchange.to_hash}"
93
+ end
94
+ end
95
+
96
+ # Whether response has successful status code and correct mandatory elements and values
97
+ RSpec::Matchers.define :be_successful do
98
+ # @param [Exchange, RestClient::Response] actual Object that returns Exchange or is Exchange
99
+ # @return [Exchange] Exchange to use
100
+ def exchange_from(actual)
101
+ actual.respond_to?(:exchange) ? actual.exchange : actual
102
+ end
103
+
104
+ # @param [Exchange, RestClient::Response] exchange Object that returns Exchange or is Exchange
105
+ # @return [Array] List of errors when checking Exchange response is successful
106
+ def collect_errors(exchange)
107
+ failure_list = []
108
+ failure_list << "#{exchange.status_code} not valid status code" unless exchange.successful_status_code?
109
+ exchange.exchange_handler.expected_mandatory_elements.each do |mandatory_element_path|
110
+ begin
111
+ exchange[mandatory_element_path]
112
+ rescue NoElementAtPath => e
113
+ failure_list << e.message
114
+ end
115
+ end
116
+ exchange.exchange_handler.expected_mandatory_xpath_values.each do |path, value|
117
+ failure_list << "Expected value at xpath '#{path}' to be '#{value}' but was '#{exchange[path]}'" unless exchange[path] == value
118
+ end
119
+ exchange.exchange_handler.expected_mandatory_json_values.each do |path, value|
120
+ failure_list << "Expected value at json '#{path}' to be '#{value}' but was '#{exchange[path]}'" unless exchange[path] == value
121
+ end
122
+ failure_list
123
+ end
124
+
125
+ match do |actual|
126
+ exchange = exchange_from actual
127
+ failure_list = collect_errors exchange
128
+ raise failure_list.to_s unless failure_list.empty?
129
+
130
+ true
131
+ end
132
+
133
+ match_when_negated do |actual|
134
+ exchange = exchange_from actual
135
+ failure_list = collect_errors exchange
136
+ raise "Expected failure. Status code is #{exchange.status_code}" if failure_list.empty?
137
+
138
+ true
139
+ end
140
+ end