soaspec 0.0.19 → 0.0.20
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/.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
|