class ApiMiniTester::TestStep
Constants
- SUPPORTED_METHODS
- SUPPORTED_RANDOM_DISTRIBUTION
- TYPE_TO_METHOD_MAP
Attributes
debug[R]
input[RW]
method[R]
name[RW]
output[RW]
results[R]
sleeps[R]
timing[RW]
uri[R]
Public Class Methods
new(base_uri, step, context = nil, data = nil, defaults = nil, debug = false)
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 25 def initialize(base_uri, step, context = nil, data = nil, defaults = nil, debug = false) @debug = debug if debug @debug_output = File.open(ApiMiniTester::TestSuite::DEBUG_FILE, 'a') end step = step.deep_merge!(defaults) do |key, this_val, other_val| if this_val.nil? other_val elsif this_val.is_a?(Array) (this_val + other_val) else this_val end end if defaults Liquid::Template.register_filter(::TestFakerFilter) @context = context uri_template = Liquid::Template.parse([base_uri, step['uri']].join("/"), error_mode: :strict) @name = step['name'] @sleeps = step['sleep'] @uri = uri_template.render( {'context' => context, 'data' => data}, { strict_variables: true }) @method = step['method'].downcase.to_sym input_template = Liquid::Template.parse(step['input'].to_yaml.to_s, error_mode: :strict) @input = YAML.load( input_template.render({'context' => context, 'data' => data}, { strict_variables: true })) output_template = Liquid::Template.parse(step['output'].to_yaml.to_s, error_mode: :strict) @output = YAML.load( output_template.render({'context' => context, 'data' => data}, { strict_variables: true })) @results = { name: step['name'], desc: step['desc'], status: [], headers: [], body: [], url: [], method: [], timing: [] } end
Public Instance Methods
add_result(section, result)
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 192 def add_result(section, result) @results[section] << result end
array_diff(a, b, path = nil, section = :body)
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 246 def array_diff(a, b, path = nil, section = :body) a.each do |a_item| if b.nil? add_result section, { result: false, name: "Response boby value: #{[path].join(".")}", desc: "Assert #{[path].join(".")} is empty" } elsif a_item.instance_of?(Hash) found = false b.each do |b_item| matching = true a_item.each_key do |k, v| matching = (b_item[k] == a_item[k]) if matching end found = true if matching end add_result section, { result: found, name: "Response body value: #{[path].join(".")}", desc: "Assert #{[path].join(".")} #{found ? 'contains' : 'does not contains'} #{a_item}" } elsif a_item.instance_of?(Array) # TODO: Add support for array of array it isn't so needed to compate so deep structures else add_result section, { result: b.include?(a_item), name: "Response boby value: #{[path].join(".")}", desc: "Assert #{[path].join(".")} #{b.include?(a_item) ? 'contains' : 'does not contains'} #{a_item}" } end end end
assert_body(response, output)
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 238 def assert_body(response, output) if output.instance_of?(Hash) hash_diff(output, response) elsif output.instance_of?(Array) array_diff(output, response) end end
assert_headers(response, output)
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 227 def assert_headers(response, output) return if output.nil? output.each do |k, v| add_result :headers, { result: (v == response[k]), name: "Header value: #{k} == #{v}", desc: "Header #{k} expected: #{v}, got #{response[k]}", exp: v, real: response[k] } end end
assert_status(response, output)
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 220 def assert_status(response, output) add_result :status, { result: (response == output), name: "Response code == #{output}", desc: "Expected response #{output}, got response #{response}", exp: output, real: response } end
assert_timing(runtime, limit = nil)
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 212 def assert_timing(runtime, limit = nil) limit ||= Float::INFINITY add_result :timing, { result: (runtime < limit), name: "Request time < #{limit}", desc: "Expected request time #{limit}, real time #{runtime}", exp: limit, real: runtime } end
body()
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 82 def body case content_type when 'application/x-www-form-urlencoded' body_to_urlencoded when 'multipart/form-data' body_to_form_data else body_by_anotations end end
body_by_anotations()
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 93 def body_by_anotations resolve_annotations(@input["body"]).to_json end
body_to_form_data()
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 103 def body_to_form_data body = {} @input["body"].each do |item| body[item['name']] = item['value'] if item['type'] == 'input' body[item['name']] = File.open(item['value'], 'r') if item['type'] == 'file' end body end
body_to_urlencoded()
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 112 def body_to_urlencoded body = [] @input["body"].each do |key, value| body << [key, value] end URI.encode_www_form(body) end
content_type()
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 72 def content_type @input['content_type'] || 'application/json' end
hash_diff(a, b, path = nil, section = :body)
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 274 def hash_diff(a, b, path = nil, section = :body) return nil if a.nil? || b.nil? a.each_key do |k, v| current_path = [path, k].join('.') if b[k].nil? add_result section, { result: false, name: "Reponse value: #{[path, k].join(".")}", desc: "Missing #{current_path}" } elsif v.instance_of?(Hash) hash_diff(a[k], b[k], current_path, section) elsif v.instance_of?(Array) array_diff(a[k], b[k], current_path, section) else add_result section, { result: (a[k] == b[k]), name: "Reponse body value: #{[path, k].join(".")}", desc: "Assert #{[path, k].join(".")}: #{a[k]} #{a[k] == b[k] ? '==' : '!='} #{b[k]}" } end end end
headers()
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 76 def headers @input['header'] = {} unless @input['header'] @input['header']['Content-Type'] ||= content_type @input['header'] end
log_debug(request, response, expectations)
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 181 def log_debug(request, response, expectations) log = { uri: uri, method: method, request: request, response: response, expectations: expectations } @debug_output.puts log.to_json end
print_results()
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 186 def print_results @results.each do |line| puts line end end
raw_body()
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 97 def raw_body @input["body"].to_hash rescue StandardError "" end
resolve_annotations(struct)
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 299 def resolve_annotations(struct) if struct.instance_of?(Hash) struct = resolve_annotations_hash(struct) end struct end
resolve_annotations_array(array, type)
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 327 def resolve_annotations_array(array, type) array.map(&type_to_method(type)) end
resolve_annotations_hash(hash)
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 306 def resolve_annotations_hash(hash) hash.each do |k, v| next unless matched = /\A\.(?<key>.*)\Z/.match(k) type = v typed_key = matched[:key] next if hash[typed_key].nil? hash[typed_key] = if hash[typed_key].instance_of?(Array) resolve_annotations_array(hash[typed_key], type) else resolve_annotations_value(hash[typed_key], type) end hash.delete(k) end end
resolve_annotations_value(value, type)
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 321 def resolve_annotations_value(value, type) value.send(type_to_method(type)) rescue StandardError value end
run_asserts(response)
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 174 def run_asserts(response) assert_status(response.code, test_status) assert_headers(response.headers, test_headers) assert_body(response.parsed_response, test_body) assert_timing(timing, test_timing) end
run_step()
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 136 def run_step if sleeps step_sleep(sleeps['before']) if sleeps['before'] end @timing = Time.now case method when :get response = HTTParty.get(uri, headers: headers) when :post response = HTTParty.post(uri, headers: headers, body: body) when :put response = HTTParty.put(uri, headers: headers, body: body) when :delete response = HTTParty.delete(uri, headers: headers) when :patch response = HTTParty.patch(uri, headers: headers, body: body) else raise "Unknown HTTP method: #{method}" end @timing = Time.now - @timing if sleeps step_sleep(sleeps['after']) if sleeps['after'] end add_result :url, { result: true, desc: "Url: #{uri}" } add_result :method, { result: true, desc: "Method: #{method}" } log_debug({ headers: headers, body: raw_body }, { headers: response.headers, body: response.parsed_response, code: response.code }, output) if debug run_asserts(response) [ results, response ] end
step_sleep(params)
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 196 def step_sleep(params) params['distribution'] = 'static' unless SUPPORTED_RANDOM_DISTRIBUTION.include?(params['distribution']) t = begin case params['distribution'] when 'static' params['value'] when 'norm' Distribution::Normal.rng(params['mean'], params['sigma'], srand).call when 'uniform' Random.new.rand(params['max'] - params['min']) + params['min'] end end sleep(t.to_f.abs) if t end
test_body()
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 124 def test_body @output['body'] end
test_headers()
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 120 def test_headers @output['header'] end
test_status()
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 128 def test_status @output['status'] end
test_timing()
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 132 def test_timing @output['timing'] end
type_to_method(type)
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 295 def type_to_method(type) TYPE_TO_METHOD_MAP[type.downcase].to_sym end
valid?()
click to toggle source
# File lib/api_mini_tester/test_step.rb, line 62 def valid? return false if uri.nil? || uri.empty? return false unless URI.parse(uri) rescue false return false unless SUPPORTED_METHODS.include? method return false if @name.nil? || @name.empty? true end