soaspec 0.0.19 → 0.0.20
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +13 -13
- data/.rspec +3 -3
- data/.travis.yml +5 -5
- data/CODE_OF_CONDUCT.md +74 -74
- data/ChangeLog +61 -55
- data/Gemfile +16 -16
- data/Gemfile.lock +115 -115
- data/LICENSE.txt +21 -21
- data/README.md +85 -66
- data/Rakefile +20 -20
- data/bin/console +14 -14
- data/bin/setup +8 -8
- data/config/data/default.yml +2 -2
- data/exe/soaspec-init +254 -252
- data/exe/xml_to_yaml_file +63 -63
- data/lib/soaspec.rb +61 -48
- data/lib/soaspec/basic_soap_handler.rb +132 -122
- data/lib/soaspec/exchange.rb +56 -56
- data/lib/soaspec/hash_methods.rb +71 -71
- data/lib/soaspec/matchers.rb +40 -39
- data/lib/soaspec/soaspec_shared_examples.rb +18 -18
- data/lib/soaspec/spec_logger.rb +17 -17
- data/lib/soaspec/tester.rb +30 -30
- data/lib/soaspec/version.rb +3 -3
- data/lib/soaspec/xpath_not_found.rb +7 -0
- data/soaspec.gemspec +34 -34
- data/template/soap_template.xml +9 -9
- metadata +4 -3
data/lib/soaspec/exchange.rb
CHANGED
@@ -1,57 +1,57 @@
|
|
1
|
-
require_relative '../soaspec'
|
2
|
-
|
3
|
-
# This represents a request / response pair
|
4
|
-
class Exchange
|
5
|
-
|
6
|
-
def initialize(name, override_parameters = {})
|
7
|
-
@test_name = name.to_s
|
8
|
-
@api_class = Soaspec::Environment.api_handler
|
9
|
-
@override_parameters = override_parameters
|
10
|
-
end
|
11
|
-
|
12
|
-
# Make request to handler with parameters defined
|
13
|
-
def make_request
|
14
|
-
@api_class.make_request(@override_parameters)
|
15
|
-
end
|
16
|
-
|
17
|
-
# Name describing this class when used with `RSpec.describe`
|
18
|
-
# This will make the request and store the response
|
19
|
-
# @return [String] Name given when initializing
|
20
|
-
def to_s
|
21
|
-
Soaspec::SpecLogger.add_to 'Example ' + @test_name
|
22
|
-
@response = make_request
|
23
|
-
@test_name
|
24
|
-
end
|
25
|
-
|
26
|
-
# Elements a shared 'success scenario' is expected to have
|
27
|
-
def mandatory_elements
|
28
|
-
@api_class.mandatory_elements
|
29
|
-
end
|
30
|
-
|
31
|
-
# Elements a shared 'success scenario' is expected to have
|
32
|
-
def mandatory_xpath_values
|
33
|
-
@api_class.mandatory_xpath_values
|
34
|
-
end
|
35
|
-
|
36
|
-
# Returns response object from Api
|
37
|
-
# For example for SOAP it will be a Savon response
|
38
|
-
# response.body (body of response as Hash)
|
39
|
-
# response.header (head of response as Hash)
|
40
|
-
def response
|
41
|
-
@response
|
42
|
-
end
|
43
|
-
|
44
|
-
# Get status code from api class. This is http response for Web Api
|
45
|
-
# @return [Integer] Status code from api class
|
46
|
-
def status_code
|
47
|
-
@api_class.status_code_for(@response)
|
48
|
-
end
|
49
|
-
|
50
|
-
# Extract value from path api class
|
51
|
-
# @param [String] path Path to return element for api class E.g - for SOAP this is XPath
|
52
|
-
# @return [String] Value at path
|
53
|
-
def [](path)
|
54
|
-
@api_class.value_from_path(self, path)
|
55
|
-
end
|
56
|
-
|
1
|
+
require_relative '../soaspec'
|
2
|
+
|
3
|
+
# This represents a request / response pair
|
4
|
+
class Exchange
|
5
|
+
|
6
|
+
def initialize(name, override_parameters = {})
|
7
|
+
@test_name = name.to_s
|
8
|
+
@api_class = Soaspec::Environment.api_handler
|
9
|
+
@override_parameters = override_parameters
|
10
|
+
end
|
11
|
+
|
12
|
+
# Make request to handler with parameters defined
|
13
|
+
def make_request
|
14
|
+
@api_class.make_request(@override_parameters)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Name describing this class when used with `RSpec.describe`
|
18
|
+
# This will make the request and store the response
|
19
|
+
# @return [String] Name given when initializing
|
20
|
+
def to_s
|
21
|
+
Soaspec::SpecLogger.add_to 'Example ' + @test_name
|
22
|
+
@response = make_request
|
23
|
+
@test_name
|
24
|
+
end
|
25
|
+
|
26
|
+
# Elements a shared 'success scenario' is expected to have
|
27
|
+
def mandatory_elements
|
28
|
+
@api_class.mandatory_elements
|
29
|
+
end
|
30
|
+
|
31
|
+
# Elements a shared 'success scenario' is expected to have
|
32
|
+
def mandatory_xpath_values
|
33
|
+
@api_class.mandatory_xpath_values
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns response object from Api
|
37
|
+
# For example for SOAP it will be a Savon response
|
38
|
+
# response.body (body of response as Hash)
|
39
|
+
# response.header (head of response as Hash)
|
40
|
+
def response
|
41
|
+
@response
|
42
|
+
end
|
43
|
+
|
44
|
+
# Get status code from api class. This is http response for Web Api
|
45
|
+
# @return [Integer] Status code from api class
|
46
|
+
def status_code
|
47
|
+
@api_class.status_code_for(@response)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Extract value from path api class
|
51
|
+
# @param [String] path Path to return element for api class E.g - for SOAP this is XPath
|
52
|
+
# @return [String] Value at path
|
53
|
+
def [](path)
|
54
|
+
@api_class.value_from_path(self, path)
|
55
|
+
end
|
56
|
+
|
57
57
|
end
|
data/lib/soaspec/hash_methods.rb
CHANGED
@@ -1,72 +1,72 @@
|
|
1
|
-
|
2
|
-
# Override Hash class with convience methods
|
3
|
-
class Hash
|
4
|
-
|
5
|
-
def self.transform_keys_to_symbols(value)
|
6
|
-
return value if not value.is_a?(Hash)
|
7
|
-
hash = value.inject({}){|memo,(k,v)| memo[k.to_sym] = Hash.transform_keys_to_symbols(v); memo}
|
8
|
-
return hash
|
9
|
-
end
|
10
|
-
|
11
|
-
# Take keys of hash and transform those to a symbols
|
12
|
-
def transform_keys_to_symbols
|
13
|
-
inject({}){|memo, (k, v)| memo[k.to_sym] = Hash.transform_keys_to_symbols(v); memo}
|
14
|
-
end
|
15
|
-
|
16
|
-
def find_all_values_for(key)
|
17
|
-
result = []
|
18
|
-
result << self[key]
|
19
|
-
self.values.each do |hash_value|
|
20
|
-
next if hash_value.is_a? Array
|
21
|
-
values = [hash_value]
|
22
|
-
values.each do |value|
|
23
|
-
result += value.find_all_values_for(key) if value.is_a? Hash
|
24
|
-
end
|
25
|
-
end
|
26
|
-
result.compact
|
27
|
-
end
|
28
|
-
|
29
|
-
# Value present in nested Hash.
|
30
|
-
def include_value?(value)
|
31
|
-
each_value do |v|
|
32
|
-
return true if v == value
|
33
|
-
next unless v.is_a? Hash
|
34
|
-
v.each_value do |v|
|
35
|
-
return true if v == value
|
36
|
-
next unless v.is_a? Hash
|
37
|
-
v.each_value do |v|
|
38
|
-
return true if v == value
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
false
|
43
|
-
end
|
44
|
-
|
45
|
-
# Whether key is present at least once
|
46
|
-
def include_key?(key)
|
47
|
-
result = find_all_values_for key
|
48
|
-
result != []
|
49
|
-
end
|
50
|
-
|
51
|
-
# Loop through each item within a key within a Hash if the key exists
|
52
|
-
# @param [Key] Key within hash to iterate through
|
53
|
-
def each_if_not_null(key)
|
54
|
-
case key.class.to_s
|
55
|
-
when 'String'
|
56
|
-
if self[key]
|
57
|
-
self[key].each do |list_item|
|
58
|
-
yield(list_item)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
when 'Array', 'Hash'
|
62
|
-
if self[key[0]]
|
63
|
-
if self[key[0]][key[1]]
|
64
|
-
self[key[0]][key[1]].each do |list_item|
|
65
|
-
yield(list_item)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
1
|
+
|
2
|
+
# Override Hash class with convience methods
|
3
|
+
class Hash
|
4
|
+
|
5
|
+
def self.transform_keys_to_symbols(value)
|
6
|
+
return value if not value.is_a?(Hash)
|
7
|
+
hash = value.inject({}){|memo,(k,v)| memo[k.to_sym] = Hash.transform_keys_to_symbols(v); memo}
|
8
|
+
return hash
|
9
|
+
end
|
10
|
+
|
11
|
+
# Take keys of hash and transform those to a symbols
|
12
|
+
def transform_keys_to_symbols
|
13
|
+
inject({}){|memo, (k, v)| memo[k.to_sym] = Hash.transform_keys_to_symbols(v); memo}
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_all_values_for(key)
|
17
|
+
result = []
|
18
|
+
result << self[key]
|
19
|
+
self.values.each do |hash_value|
|
20
|
+
next if hash_value.is_a? Array
|
21
|
+
values = [hash_value]
|
22
|
+
values.each do |value|
|
23
|
+
result += value.find_all_values_for(key) if value.is_a? Hash
|
24
|
+
end
|
25
|
+
end
|
26
|
+
result.compact
|
27
|
+
end
|
28
|
+
|
29
|
+
# Value present in nested Hash.
|
30
|
+
def include_value?(value)
|
31
|
+
each_value do |v|
|
32
|
+
return true if v == value
|
33
|
+
next unless v.is_a? Hash
|
34
|
+
v.each_value do |v|
|
35
|
+
return true if v == value
|
36
|
+
next unless v.is_a? Hash
|
37
|
+
v.each_value do |v|
|
38
|
+
return true if v == value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
# Whether key is present at least once
|
46
|
+
def include_key?(key)
|
47
|
+
result = find_all_values_for key
|
48
|
+
result != []
|
49
|
+
end
|
50
|
+
|
51
|
+
# Loop through each item within a key within a Hash if the key exists
|
52
|
+
# @param [Key] Key within hash to iterate through
|
53
|
+
def each_if_not_null(key)
|
54
|
+
case key.class.to_s
|
55
|
+
when 'String'
|
56
|
+
if self[key]
|
57
|
+
self[key].each do |list_item|
|
58
|
+
yield(list_item)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
when 'Array', 'Hash'
|
62
|
+
if self[key[0]]
|
63
|
+
if self[key[0]][key[1]]
|
64
|
+
self[key[0]][key[1]].each do |list_item|
|
65
|
+
yield(list_item)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
72
|
end
|
data/lib/soaspec/matchers.rb
CHANGED
@@ -1,40 +1,41 @@
|
|
1
|
-
|
2
|
-
require_relative 'hash_methods'
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
1
|
+
|
2
|
+
require_relative 'hash_methods'
|
3
|
+
require_relative 'xpath_not_found'
|
4
|
+
|
5
|
+
RSpec::Matchers.define :contain_value do |expected|
|
6
|
+
match do |actual|
|
7
|
+
expect(actual.response.body.include_value?(expected)).to be true
|
8
|
+
end
|
9
|
+
|
10
|
+
failure_message do |actual|
|
11
|
+
"expected that #{actual.response.body} would contain value #{expected}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
RSpec::Matchers.define :contain_key do |expected|
|
16
|
+
match do |actual|
|
17
|
+
expect(actual.response.body.include_key?(expected)).to be true
|
18
|
+
end
|
19
|
+
|
20
|
+
failure_message do |actual|
|
21
|
+
"expected that #{actual.response.body} would contain key #{expected}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
RSpec::Matchers.define :have_element_at_xpath do |xpath|
|
26
|
+
match do |exchange|
|
27
|
+
expect { exchange[xpath] }.to_not raise_error NoElementAtXpath
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
RSpec::Matchers.define :have_xpath_value do |expected_hash|
|
32
|
+
match do |exchange|
|
33
|
+
expected_hash = Hash[*expected_hash.flatten] if expected_hash.is_a?(Array) # For some reason Array was occuring
|
34
|
+
expect(exchange[expected_hash.keys.first]).to eq expected_hash.values.first
|
35
|
+
end
|
36
|
+
|
37
|
+
failure_message do |actual|
|
38
|
+
"expected that xpath '#{expected_hash.keys.first}' has value '#{expected_hash.values.first}' but was '#{actual[expected_hash.keys.first]}'"
|
39
|
+
end
|
40
|
+
|
40
41
|
end
|
@@ -1,19 +1,19 @@
|
|
1
|
-
require 'rspec'
|
2
|
-
|
3
|
-
shared_examples_for 'success scenario' do
|
4
|
-
it 'has status code of 200' do
|
5
|
-
expect(described_class.status_code).to eq 200
|
6
|
-
end
|
7
|
-
context 'has expected mandatory elements' do
|
8
|
-
described_class.mandatory_elements.each do |mandatory_element|
|
9
|
-
it mandatory_element do
|
10
|
-
expect(described_class).to contain_key mandatory_element
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
described_class.mandatory_xpath_values.each do |xpath, value|
|
15
|
-
it "has xpath '#{xpath}' equal to '#{value}'" do
|
16
|
-
expect(described_class).to have_xpath_value(xpath => value)
|
17
|
-
end
|
18
|
-
end
|
1
|
+
require 'rspec'
|
2
|
+
|
3
|
+
shared_examples_for 'success scenario' do
|
4
|
+
it 'has status code of 200' do
|
5
|
+
expect(described_class.status_code).to eq 200
|
6
|
+
end
|
7
|
+
context 'has expected mandatory elements' do
|
8
|
+
described_class.mandatory_elements.each do |mandatory_element|
|
9
|
+
it mandatory_element do
|
10
|
+
expect(described_class).to contain_key mandatory_element
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
described_class.mandatory_xpath_values.each do |xpath, value|
|
15
|
+
it "has xpath '#{xpath}' equal to '#{value}'" do
|
16
|
+
expect(described_class).to have_xpath_value(xpath => value)
|
17
|
+
end
|
18
|
+
end
|
19
19
|
end
|
data/lib/soaspec/spec_logger.rb
CHANGED
@@ -1,18 +1,18 @@
|
|
1
|
-
|
2
|
-
module Soaspec
|
3
|
-
require 'logger'
|
4
|
-
|
5
|
-
# Handles logs of API requests and responses
|
6
|
-
class SpecLogger
|
7
|
-
def self.create
|
8
|
-
@logger = Logger.new('logs/traffic.log') # Where request and responses of APIs are stored
|
9
|
-
@logger.level = Logger::DEBUG
|
10
|
-
@logger
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.add_to(message)
|
14
|
-
@logger.info(message)
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|
1
|
+
|
2
|
+
module Soaspec
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
# Handles logs of API requests and responses
|
6
|
+
class SpecLogger
|
7
|
+
def self.create
|
8
|
+
@logger = Logger.new('logs/traffic.log') # Where request and responses of APIs are stored
|
9
|
+
@logger.level = Logger::DEBUG
|
10
|
+
@logger
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.add_to(message)
|
14
|
+
@logger.info(message)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
18
|
end
|
data/lib/soaspec/tester.rb
CHANGED
@@ -1,31 +1,31 @@
|
|
1
|
-
|
2
|
-
module Soaspec
|
3
|
-
# Has basic methods common for methods defining RSpec tests in YAML
|
4
|
-
class Tester
|
5
|
-
|
6
|
-
# Retrieve the name of the template file to be used in the API request
|
7
|
-
attr_reader :template_name
|
8
|
-
|
9
|
-
# Set instance variable name
|
10
|
-
# @param [String, Symbol] name Name used when describing API test
|
11
|
-
# @param [Hash] options Parameters defining
|
12
|
-
def initialize(name, options)
|
13
|
-
@name = name
|
14
|
-
end
|
15
|
-
|
16
|
-
# Sets api handler variable globally. This is used in 'Exchange' class
|
17
|
-
# @return [String] Name set upon initialisation
|
18
|
-
def to_s
|
19
|
-
Soaspec::Environment.api_handler = self
|
20
|
-
@name
|
21
|
-
end
|
22
|
-
|
23
|
-
# Set the request option type and the template name
|
24
|
-
# @param [String] name Name of file inside 'template' folder excluding extension
|
25
|
-
def template_name=(name)
|
26
|
-
@request_option = :template
|
27
|
-
@template_name = name
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
1
|
+
|
2
|
+
module Soaspec
|
3
|
+
# Has basic methods common for methods defining RSpec tests in YAML
|
4
|
+
class Tester
|
5
|
+
|
6
|
+
# Retrieve the name of the template file to be used in the API request
|
7
|
+
attr_reader :template_name
|
8
|
+
|
9
|
+
# Set instance variable name
|
10
|
+
# @param [String, Symbol] name Name used when describing API test
|
11
|
+
# @param [Hash] options Parameters defining
|
12
|
+
def initialize(name, options)
|
13
|
+
@name = name
|
14
|
+
end
|
15
|
+
|
16
|
+
# Sets api handler variable globally. This is used in 'Exchange' class
|
17
|
+
# @return [String] Name set upon initialisation
|
18
|
+
def to_s
|
19
|
+
Soaspec::Environment.api_handler = self
|
20
|
+
@name
|
21
|
+
end
|
22
|
+
|
23
|
+
# Set the request option type and the template name
|
24
|
+
# @param [String] name Name of file inside 'template' folder excluding extension
|
25
|
+
def template_name=(name)
|
26
|
+
@request_option = :template
|
27
|
+
@template_name = name
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
31
|
end
|