Class: ApiMiniTester::TestStep

Inherits:
Object
  • Object
show all
Includes:
HTTParty
Defined in:
lib/api_mini_tester/test_step.rb

Constant Summary collapse

SUPPORTED_METHODS =
i[ get post put delete ].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base_uri, step, context = nil, data = nil) ⇒ TestStep

Returns a new instance of TestStep.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/api_mini_tester/test_step.rb', line 19

def initialize(base_uri, step, context = nil, data = nil)
  Liquid::Template.register_filter(::TestFakerFilter)
  @context = context
  uri_template = Liquid::Template.parse([base_uri, step['uri']].join("/"), error_mode: :strict)
  @name = step['name']

  @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

Instance Attribute Details

#inputObject

Returns the value of attribute input.



16
17
18
# File 'lib/api_mini_tester/test_step.rb', line 16

def input
  @input
end

#methodObject

Returns the value of attribute method.



16
17
18
# File 'lib/api_mini_tester/test_step.rb', line 16

def method
  @method
end

#nameObject

Returns the value of attribute name.



16
17
18
# File 'lib/api_mini_tester/test_step.rb', line 16

def name
  @name
end

#outputObject

Returns the value of attribute output.



16
17
18
# File 'lib/api_mini_tester/test_step.rb', line 16

def output
  @output
end

#resultsObject (readonly)

Returns the value of attribute results.



17
18
19
# File 'lib/api_mini_tester/test_step.rb', line 17

def results
  @results
end

#uriObject

Returns the value of attribute uri.



16
17
18
# File 'lib/api_mini_tester/test_step.rb', line 16

def uri
  @uri
end

Instance Method Details

#add_result(section, result) ⇒ Object



125
126
127
# File 'lib/api_mini_tester/test_step.rb', line 125

def add_result(section, result)
  @results[section] << result
end

#array_diff(a, b, path = nil, section = :body) ⇒ Object



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/api_mini_tester/test_step.rb', line 162

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) ⇒ Object



154
155
156
157
158
159
160
# File 'lib/api_mini_tester/test_step.rb', line 154

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) ⇒ Object



144
145
146
147
148
149
150
151
152
# File 'lib/api_mini_tester/test_step.rb', line 144

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) ⇒ Object



137
138
139
140
141
142
# File 'lib/api_mini_tester/test_step.rb', line 137

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) ⇒ Object



129
130
131
132
133
134
135
# File 'lib/api_mini_tester/test_step.rb', line 129

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

#bodyObject



58
59
60
61
62
63
64
65
66
67
# File 'lib/api_mini_tester/test_step.rb', line 58

def body
  case content_type
  when 'application/x-www-form-urlencoded'
    body_to_form_data
  when 'multipart/form-data'
    body_to_form_data
  else
    @input["body"].to_json
  end
end

#body_to_form_dataObject



69
70
71
72
73
74
75
76
# File 'lib/api_mini_tester/test_step.rb', line 69

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

#content_typeObject



49
50
51
# File 'lib/api_mini_tester/test_step.rb', line 49

def content_type
  @input['content_type'] || 'application/json'
end

#hash_diff(a, b, path = nil, section = :body) ⇒ Object



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/api_mini_tester/test_step.rb', line 190

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

#headersObject



53
54
55
56
# File 'lib/api_mini_tester/test_step.rb', line 53

def headers
  @input['header']['Content-type'] = content_type if content_type == 'application/json'
  @input['header']
end


119
120
121
122
123
# File 'lib/api_mini_tester/test_step.rb', line 119

def print_results
  @results.each do |line|
    puts line
  end
end

#run_stepObject



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/api_mini_tester/test_step.rb', line 94

def run_step
  @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)
  end
  @timing = Time.now - @timing

  add_result :url, { result: true, desc: "Url: #{uri}" }
  add_result :method, { result: true, desc: "Method: #{method}" }

  assert_status(response.code, test_status)
  assert_headers(response.headers, test_headers)
  assert_body(response.parsed_response, test_body)
  assert_timing(@timing, test_timing)

  [ results, response ]
end

#test_bodyObject



82
83
84
# File 'lib/api_mini_tester/test_step.rb', line 82

def test_body
  @output['body']
end

#test_headersObject



78
79
80
# File 'lib/api_mini_tester/test_step.rb', line 78

def test_headers
  @output['header']
end

#test_statusObject



86
87
88
# File 'lib/api_mini_tester/test_step.rb', line 86

def test_status
  @output['status']
end

#test_timingObject



90
91
92
# File 'lib/api_mini_tester/test_step.rb', line 90

def test_timing
  @output['timing']
end

#valid?Boolean

Returns:

  • (Boolean)


41
42
43
44
45
46
47
# File 'lib/api_mini_tester/test_step.rb', line 41

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