soaspec 0.2.11 → 0.2.12
Sign up to get free protection for your applications and to get access to all the features.
- 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
|