soaspec 0.2.11 → 0.2.12
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/ChangeLog +4 -0
- data/README.md +9 -0
- data/Rakefile +8 -0
- data/Todo.md +3 -1
- data/lib/soaspec/core_ext/hash.rb +3 -49
- data/lib/soaspec/exchange/exchange.rb +2 -0
- data/lib/soaspec/exchange/exchange_properties.rb +1 -0
- data/lib/soaspec/exchange/request_builder.rb +10 -2
- data/lib/soaspec/exchange_handlers/exchange_handler.rb +5 -4
- data/lib/soaspec/exchange_handlers/handler_accessors.rb +14 -8
- data/lib/soaspec/exchange_handlers/response_extractor.rb +1 -0
- data/lib/soaspec/exchange_handlers/soap_handler.rb +1 -1
- data/lib/soaspec/o_auth2.rb +1 -1
- data/lib/soaspec/spec_logger.rb +6 -1
- data/lib/soaspec/version.rb +1 -1
- data/lib/soaspec/wsdl_generator.rb +27 -26
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 70554212f6b7e667767a9d38f2c3b9c2f0bfec96
|
4
|
+
data.tar.gz: 3eaae9121a99e8b9a48b4f25ecb5a17178158e7c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c7f0b3de2c2c31778410004db25705e3a561617be0e227be1c913e7b22e741b471fea0ce5ce2e575ea8f134dc39db5e28f9bbc61d7a0ccb17bace96e94a7500
|
7
|
+
data.tar.gz: 5deb9f12a6b63ec3e0077ca84c79ff625ee90e0cd86a7fadc60924ea0b3e98b8b85fd6274304ac2b54c930c960ec229dc9c3dbd0c2595de17a3fecea62541535
|
data/ChangeLog
CHANGED
data/README.md
CHANGED
@@ -175,6 +175,15 @@ expect(@exchange['message']).to include 'success'
|
|
175
175
|
expect(@exchange.status_code).to eq 200
|
176
176
|
```
|
177
177
|
|
178
|
+
## Logging
|
179
|
+
|
180
|
+
By default traffic is logged to a file in the `logs` folder called `traffic_DATETIME.log`.
|
181
|
+
|
182
|
+
The `SpecLogger` class is responsible for handling all the logs.
|
183
|
+
|
184
|
+
To change the output file location to 'custom.log':
|
185
|
+
`Soaspec::SpecLogger.traffic_file = 'custom.log'`
|
186
|
+
|
178
187
|
## Development
|
179
188
|
|
180
189
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/Rakefile
CHANGED
@@ -28,3 +28,11 @@ end
|
|
28
28
|
YARD::Rake::YardocTask.new do |t|
|
29
29
|
t.files = %w[features/**/*.feature features/**/*.rb lib/soaspec/cucumber/*.rb] # lib/soaspec/cucumber/*.rb]
|
30
30
|
end
|
31
|
+
|
32
|
+
desc 'Ensure system has all docs'
|
33
|
+
task :must_have_docs do
|
34
|
+
yard = `yard`
|
35
|
+
puts yard
|
36
|
+
percentage = yard.lines.last.strip.split('%').first.to_f
|
37
|
+
raise 'Must be fully documented' unless percentage == 100.00
|
38
|
+
end
|
data/Todo.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
*
|
1
|
+
* `yard` should show everything documented
|
2
|
+
* Rubocop should have 0 offenses
|
2
3
|
* Unit tests
|
3
4
|
* OAuth class, etc
|
5
|
+
* Get initial `soaspec new` working with TODOs as placeholders for how to get started
|
4
6
|
* Request method from within exchange
|
5
7
|
* Use this in tests
|
6
8
|
* Basic service generator
|
@@ -1,6 +1,7 @@
|
|
1
|
-
# Override Hash class with
|
1
|
+
# Override Hash class with convenience methods
|
2
2
|
class Hash
|
3
3
|
# Transform each key in Hash to a symbol. Privately used by non-self method
|
4
|
+
# @param [Object] value Value inside hash to transform keys under
|
4
5
|
def self.transform_keys_to_symbols(value)
|
5
6
|
return value unless value.is_a?(Hash)
|
6
7
|
|
@@ -13,28 +14,8 @@ class Hash
|
|
13
14
|
each_with_object({}) { |(k, v), memo| memo[k.to_sym] = Hash.transform_keys_to_symbols(v); }
|
14
15
|
end
|
15
16
|
|
16
|
-
# Returns all keys that have a particular value within a nested Hash
|
17
|
-
def find_all_values_for(key)
|
18
|
-
result = []
|
19
|
-
result << self[key]
|
20
|
-
values.each do |hash_value|
|
21
|
-
# next if hash_value.is_a? Array
|
22
|
-
#
|
23
|
-
if hash_value.is_a?(Array)
|
24
|
-
hash_value.each do |array_element|
|
25
|
-
result += array_element.find_all_values_for(key) if array_element.is_a? Hash
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
values = [hash_value]
|
30
|
-
values.each do |value|
|
31
|
-
result += value.find_all_values_for(key) if value.is_a? Hash
|
32
|
-
end
|
33
|
-
end
|
34
|
-
result.compact
|
35
|
-
end
|
36
|
-
|
37
17
|
# Value present in nested Hash.
|
18
|
+
# @return [Boolean] Whether value is included in nested Hash
|
38
19
|
def include_value?(value)
|
39
20
|
each_value do |v|
|
40
21
|
return true if v == value
|
@@ -51,31 +32,4 @@ class Hash
|
|
51
32
|
end
|
52
33
|
false
|
53
34
|
end
|
54
|
-
|
55
|
-
# # Whether key is present at least once
|
56
|
-
# def include_key?(key)
|
57
|
-
# result = find_all_values_for key
|
58
|
-
# result != []
|
59
|
-
# end
|
60
|
-
|
61
|
-
# Loop through each item within a key within a Hash if the key exists
|
62
|
-
# @param [Key] key Key within hash to iterate through
|
63
|
-
def each_if_not_null(key)
|
64
|
-
case key.class.to_s
|
65
|
-
when 'String'
|
66
|
-
if self[key]
|
67
|
-
self[key].each do |list_item|
|
68
|
-
yield(list_item)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
when 'Array', 'Hash'
|
72
|
-
if self[key[0]]
|
73
|
-
if self[key[0]][key[1]]
|
74
|
-
self[key[0]][key[1]].each do |list_item|
|
75
|
-
yield(list_item)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
35
|
end
|
@@ -43,6 +43,8 @@ class Exchange
|
|
43
43
|
nil
|
44
44
|
end
|
45
45
|
|
46
|
+
# Create new Exchange according to parameters set. A response will be made if called
|
47
|
+
# explicitly with 'response' method or through other methods that use it like 'status_code'
|
46
48
|
# @param [Symbol, String] name Name shown in RSpec run
|
47
49
|
# @param [Hash] override_parameters Parameters to override for default params
|
48
50
|
def initialize(name = self.class.to_s, override_parameters = {})
|
@@ -17,6 +17,7 @@ module Soaspec
|
|
17
17
|
end
|
18
18
|
|
19
19
|
# Set retry_for_success to true, retrying response until a successful status code is returned
|
20
|
+
# @param [Integer] retry_count Times to retry to get a positive response
|
20
21
|
def expect_positive_status(retry_count: 3)
|
21
22
|
define_method('retry_count') do
|
22
23
|
retry_count
|
@@ -16,6 +16,12 @@ module Soaspec
|
|
16
16
|
# Set a parameter request in the request body.
|
17
17
|
# Can be used to build a request over several steps (e.g Cucumber)
|
18
18
|
# Will be used with FactoryBot
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# exchange['name'] = 'tester'
|
22
|
+
# # Will set { name: tester } in the response, formatting as JSON or XML depending on REST / SOAP
|
23
|
+
# @param [String, Symbol] key Name of request element to set
|
24
|
+
# @param [String] value Value to set request element to
|
19
25
|
def []=(key, value)
|
20
26
|
@override_parameters[:body] ||= {}
|
21
27
|
@override_parameters[:body][key] = value
|
@@ -24,7 +30,7 @@ module Soaspec
|
|
24
30
|
# Implement undefined setter with []= for FactoryBot to use without needing to define params to set
|
25
31
|
# @param [Object] method_name Name of method not defined
|
26
32
|
# @param [Object] args Arguments passed to method
|
27
|
-
# @param [Object] block
|
33
|
+
# @param [Object] block Block passed to method
|
28
34
|
def method_missing(method_name, *args, &block)
|
29
35
|
set_value = args.first
|
30
36
|
if method_name[-1] == '=' # A setter method
|
@@ -41,13 +47,15 @@ module Soaspec
|
|
41
47
|
end
|
42
48
|
|
43
49
|
# Used for setters that are not defined
|
50
|
+
# @param [String] method_name Name of method
|
51
|
+
# @param [Array] args List of arguments to method
|
44
52
|
def respond_to_missing?(method_name, *args)
|
45
53
|
method_name[-1] == '=' || super
|
46
54
|
end
|
47
55
|
|
48
56
|
# Makes request, caching the response and returning self
|
49
57
|
# Used by FactoryBot
|
50
|
-
# @return [Self]
|
58
|
+
# @return [Self] Return itself so methods can be called on itself afterwards
|
51
59
|
def save!
|
52
60
|
@retry_for_success = @fail_factory ? false : true
|
53
61
|
call
|
@@ -16,7 +16,7 @@ module Soaspec
|
|
16
16
|
|
17
17
|
# Set instance variable name
|
18
18
|
# @param [String, Symbol] name Name used when describing API test
|
19
|
-
# @param [Hash]
|
19
|
+
# @param [Hash] _options Parameters defining handler. Used in descendants
|
20
20
|
def initialize(name = self.class.to_s, _options = {})
|
21
21
|
use
|
22
22
|
@request_option = :hash
|
@@ -86,9 +86,7 @@ module Soaspec
|
|
86
86
|
# @param [Symbol] name Name of method to use to access this value within handler
|
87
87
|
# @param [String] value Value to store
|
88
88
|
def store(name, value)
|
89
|
-
define_singleton_method('__stored_val__' + name.to_s)
|
90
|
-
value
|
91
|
-
end
|
89
|
+
define_singleton_method('__stored_val__' + name.to_s) { value }
|
92
90
|
end
|
93
91
|
|
94
92
|
# Set instance variable for each key in hash remove it from Hash
|
@@ -99,6 +97,8 @@ module Soaspec
|
|
99
97
|
end
|
100
98
|
|
101
99
|
# Set instance variable and remove it from Hash
|
100
|
+
# @param [Hash] hash Hash to remove/retrieve keys from
|
101
|
+
# @param [String,Symbol] key Key to remove and to set instance variable for
|
102
102
|
def set_remove_key(hash, key)
|
103
103
|
return unless hash.key? key
|
104
104
|
|
@@ -112,6 +112,7 @@ module Soaspec
|
|
112
112
|
end
|
113
113
|
|
114
114
|
# Request of API call. Either intended request or actual request
|
115
|
+
# @param [Object] response Response from calling exchange
|
115
116
|
def request(response)
|
116
117
|
return "Request not yet sent Request option is #{@request_option}" unless response
|
117
118
|
|
@@ -31,11 +31,13 @@ module Soaspec
|
|
31
31
|
# @example Inside class
|
32
32
|
# mandatory_xpath_values '//xmlns:GetWeatherResult' => 'Data Not Found'
|
33
33
|
#
|
34
|
-
# In test
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
34
|
+
# @example In test
|
35
|
+
# describe Exchange(:name) do
|
36
|
+
# it_behaves_like 'success scenario' # Includes xpath pair validation
|
37
|
+
# end
|
38
38
|
#
|
39
|
+
# @param [Hash] xpath_value_pairs Hash of element => expected value that must appear
|
40
|
+
# in a successful response body
|
39
41
|
def mandatory_xpath_values(xpath_value_pairs)
|
40
42
|
raise ArgumentError('Hash of {xpath => expected values} expected ') unless xpath_value_pairs.is_a? Hash
|
41
43
|
|
@@ -47,11 +49,13 @@ module Soaspec
|
|
47
49
|
# @example Inside class
|
48
50
|
# mandatory_json_values '$..GetWeatherResult' => 'Found'
|
49
51
|
#
|
50
|
-
# In test
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
52
|
+
# @example In test
|
53
|
+
# describe Exchange(:name) do
|
54
|
+
# it_behaves_like 'success scenario' # Includes json pair validation
|
55
|
+
# end
|
54
56
|
#
|
57
|
+
# @param [Hash] json_value_pairs Hash of element => expected value that must appear
|
58
|
+
# in a successful response body
|
55
59
|
def mandatory_json_values(json_value_pairs)
|
56
60
|
raise ArgumentError("Hash of {'jsonpath' => expected values} expected") unless json_value_pairs.is_a? Hash
|
57
61
|
|
@@ -84,6 +88,7 @@ module Soaspec
|
|
84
88
|
|
85
89
|
# All xpath will be done with XML that is converted to lower case
|
86
90
|
# You must then use lower case in the xpath's to obtain the desired values
|
91
|
+
# @param [Boolean] set Whether to convert all xml in response to lower case before performing XPath
|
87
92
|
def convert_to_lower(set)
|
88
93
|
return unless set
|
89
94
|
|
@@ -94,6 +99,7 @@ module Soaspec
|
|
94
99
|
# For why this may not be a good thing in general see
|
95
100
|
# http://tenderlovemaking.com/2009/04/23/namespaces-in-xml.html
|
96
101
|
# This will be overridden if xpath has a ':' in it
|
102
|
+
# @param [Boolean] set Whether to strip namespaces form XML response
|
97
103
|
def strip_namespaces(set)
|
98
104
|
return unless set
|
99
105
|
|
@@ -145,7 +145,7 @@ module Soaspec
|
|
145
145
|
end
|
146
146
|
|
147
147
|
# Convert all XML nodes to lowercase
|
148
|
-
# @param [Nokogiri::XML::Document]
|
148
|
+
# @param [Nokogiri::XML::Document] xml_doc Xml document to convert
|
149
149
|
def convert_to_lower_case(xml_doc)
|
150
150
|
xml_doc.traverse do |node|
|
151
151
|
node.name = node.name.downcase if node.is_a?(Nokogiri::XML::Element)
|
data/lib/soaspec/o_auth2.rb
CHANGED
@@ -48,7 +48,6 @@ module Soaspec
|
|
48
48
|
params[:security_token] = ERB.new(params[:security_token]).result(binding) if params[:security_token]
|
49
49
|
params[:token_url] = ERB.new(params[:token_url]).result(binding) if params[:token_url]
|
50
50
|
params[:password] = ERB.new(params[:password]).result(binding) if params[:password]
|
51
|
-
Soaspec::SpecLogger.info request_message
|
52
51
|
end
|
53
52
|
|
54
53
|
# Retrieve instance_url according to access token response.
|
@@ -61,6 +60,7 @@ module Soaspec
|
|
61
60
|
|
62
61
|
# @return [String] Existing or new access token, dependent on refresh_token attribute
|
63
62
|
def access_token
|
63
|
+
Soaspec::SpecLogger.info request_message
|
64
64
|
case Soaspec::OAuth2.refresh_token
|
65
65
|
when :once
|
66
66
|
Soaspec::OAuth2.access_tokens[params] ||= response['access_token']
|
data/lib/soaspec/spec_logger.rb
CHANGED
@@ -23,12 +23,15 @@ module Soaspec
|
|
23
23
|
@output_to_file = true
|
24
24
|
# Time test run. Will only be calculated once once called
|
25
25
|
@time_test_run = Time.now.strftime('%Y-%m-%d_%H_%M_%S')
|
26
|
-
|
26
|
+
# By default file is based on time
|
27
|
+
@traffic_file = nil
|
27
28
|
class << self
|
28
29
|
# Folder to put API traffic logs
|
29
30
|
attr_accessor :traffic_folder
|
30
31
|
# Readers for log parameters
|
31
32
|
attr_reader :output_to_terminal, :output_to_file, :time_test_run
|
33
|
+
# Set file to log traffic to
|
34
|
+
attr_writer :traffic_file
|
32
35
|
|
33
36
|
# Whether to log all API traffic
|
34
37
|
def log_api_traffic=(set)
|
@@ -55,6 +58,8 @@ module Soaspec
|
|
55
58
|
|
56
59
|
# @return [String] Traffic file to create logs at
|
57
60
|
def traffic_file
|
61
|
+
return File.join(traffic_folder, @traffic_file) if @traffic_file
|
62
|
+
|
58
63
|
filename = "traffic_#{time_test_run}.log"
|
59
64
|
File.join(traffic_folder, filename)
|
60
65
|
end
|
data/lib/soaspec/version.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module Soaspec
|
2
|
+
# Produce test content from a WSDL
|
2
3
|
module WsdlGenerator
|
3
4
|
# Attempt to calculate values of enumeration by looking up type in Schema
|
5
|
+
# @param [String] type Try out filling enumeration for type. Return Custom Type if can't be done
|
4
6
|
def try_enum_for(type)
|
5
7
|
raise "'@wsdl_schemas' must be defined" if @wsdl_schemas.nil?
|
6
8
|
|
@@ -28,34 +30,33 @@ module Soaspec
|
|
28
30
|
|
29
31
|
# Return value of string after a namespace
|
30
32
|
# @param [String] string String to parse for part after namespace
|
33
|
+
# @return [String] Part after the namespace, demonstrated by ':'
|
31
34
|
def value_after_namespace(string)
|
32
35
|
string.split(':').last
|
33
36
|
end
|
34
37
|
|
35
38
|
# Based on WSDL type return a valid value
|
36
39
|
# @param [String] type Type without the WSDL
|
40
|
+
# @return [Object] Value representing type to fill in
|
37
41
|
def fill_in_field_from_type(type)
|
38
42
|
case type
|
39
|
-
when 'string'
|
40
|
-
|
41
|
-
when '
|
42
|
-
|
43
|
-
when 'boolean'
|
44
|
-
true
|
45
|
-
when 'double'
|
46
|
-
'1.5'
|
43
|
+
when 'string' then options[:string_default] # 'test string'
|
44
|
+
when 'int' then 2
|
45
|
+
when 'boolean' then true
|
46
|
+
when 'double' then '1.5'
|
47
47
|
else
|
48
48
|
try_enum_for type
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
52
|
# @param [Nokogiri::XML::Element]
|
53
|
-
# @return [Boolean] True if
|
53
|
+
# @return [Boolean] True if Nokogiri element is a complex type, that is, has a complexType element underneath itself
|
54
54
|
def complex_type?(element)
|
55
55
|
element.children.any? { |child| child.name == 'complexType' }
|
56
56
|
end
|
57
57
|
|
58
58
|
# @param [String, Symbol] underscore_separated Snakecase value to be converted to camel case
|
59
|
+
# @return [String] CamelCased value
|
59
60
|
def camel_case(underscore_separated)
|
60
61
|
underscore_separated.to_s.split('_').collect(&:capitalize).join
|
61
62
|
end
|
@@ -81,7 +82,9 @@ module Soaspec
|
|
81
82
|
end
|
82
83
|
end
|
83
84
|
|
84
|
-
#
|
85
|
+
# Adds documentation content for all children of XML element
|
86
|
+
# @param [Nokogiri::XML::Element] element Type to document for
|
87
|
+
# @param [Integer] depth How many times to iterate depth for
|
85
88
|
def document_type_for(element, depth = 1)
|
86
89
|
# raise "Too far deep for #{element}" unless depth < 6
|
87
90
|
return unless depth < 6
|
@@ -104,20 +107,18 @@ module Soaspec
|
|
104
107
|
end
|
105
108
|
|
106
109
|
# Makes a yaml string in a '@content' instance variable
|
107
|
-
# @param [Nokogiri::XML::NodeSet] list List
|
110
|
+
# @param [Nokogiri::XML::NodeSet] list List to convert to YAML
|
108
111
|
def wsdl_to_yaml_for(list)
|
109
112
|
raise "'@content' string must be set" if @content.nil?
|
110
113
|
|
111
|
-
list.each
|
112
|
-
document_type_for element
|
113
|
-
end
|
114
|
+
list.each { |element| document_type_for element }
|
114
115
|
end
|
115
116
|
|
116
117
|
# Prompt user for wsdl
|
117
118
|
def ask_wsdl
|
118
|
-
prompt = <<-
|
119
|
-
Enter WSDL:
|
120
|
-
|
119
|
+
prompt = <<-WSDL_LOC
|
120
|
+
Enter WSDL:
|
121
|
+
WSDL_LOC
|
121
122
|
print prompt.chop
|
122
123
|
@wsdl = $stdin.gets.strip
|
123
124
|
puts
|
@@ -125,9 +126,9 @@ Enter WSDL:
|
|
125
126
|
|
126
127
|
# Prompt user for Service name for wsdl
|
127
128
|
def name_of_wsdl
|
128
|
-
prompt = <<-
|
129
|
-
Enter what you would like to name WSDL (CamelCase):
|
130
|
-
|
129
|
+
prompt = <<-WSDL_NAME
|
130
|
+
Enter what you would like to name WSDL (CamelCase):
|
131
|
+
WSDL_NAME
|
131
132
|
print prompt.chop
|
132
133
|
@name = $stdin.gets.strip
|
133
134
|
puts
|
@@ -135,16 +136,16 @@ Enter what you would like to name WSDL (CamelCase):
|
|
135
136
|
|
136
137
|
# Prompt user to enter basic auth details
|
137
138
|
def enter_auth_details
|
138
|
-
prompt = <<-
|
139
|
-
User Name:
|
140
|
-
|
139
|
+
prompt = <<-AUTH_PROMPT
|
140
|
+
User Name:
|
141
|
+
AUTH_PROMPT
|
141
142
|
print prompt.chop
|
142
143
|
@auth_name = $stdin.gets.strip
|
143
144
|
puts
|
144
145
|
|
145
|
-
prompt = <<-
|
146
|
-
User Password:
|
147
|
-
|
146
|
+
prompt = <<-PASSWORD
|
147
|
+
User Password:
|
148
|
+
PASSWORD
|
148
149
|
print prompt.chop
|
149
150
|
@auth_password = $stdin.gets.strip
|
150
151
|
puts
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: soaspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- SamuelGarrattIQA
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-02-
|
11
|
+
date: 2019-02-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|