apirunner 0.1.8 → 0.1.9

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.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.8
1
+ 0.1.9
data/apirunner.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{apirunner}
8
- s.version = "0.1.8"
8
+ s.version = "0.1.9"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["jan@moviepilot.com"]
12
- s.date = %q{2010-09-23}
12
+ s.date = %q{2010-09-27}
13
13
  s.description = %q{apirunner is a testsuite to query your RESTful JSON API and match response with your defined expectations}
14
14
  s.email = %q{developers@moviepilot.com}
15
15
  s.extra_rdoc_files = [
@@ -34,11 +34,13 @@ Gem::Specification.new do |s|
34
34
  "features/apirunner.feature",
35
35
  "features/step_definitions/apirunner_steps.rb",
36
36
  "features/support/env.rb",
37
+ "lib/api_configuration.rb",
37
38
  "lib/api_runner.rb",
38
39
  "lib/apirunner.rb",
39
40
  "lib/apirunner/railtie.rb",
40
41
  "lib/expectation_matcher.rb",
41
42
  "lib/http_client.rb",
43
+ "lib/result.rb",
42
44
  "lib/tasks/api.rake",
43
45
  "spec/.rspec",
44
46
  "spec/api_runner_spec.rb",
@@ -13,3 +13,9 @@ production:
13
13
  host: www.moviepilot.com
14
14
  port: 80
15
15
  namespace: api1v0
16
+ general:
17
+ verbosity:
18
+ - rspec
19
+ - verbose_on_error
20
+ - verbose_on_success
21
+ - verbose_with_curl
@@ -0,0 +1,6 @@
1
+ class ApiConfiguration
2
+
3
+ attr_accessor :protocol, :host, :namespace, :port, :verbosity
4
+
5
+ end
6
+
data/lib/api_runner.rb CHANGED
@@ -2,6 +2,7 @@ class ApiRunner
2
2
  require 'yaml'
3
3
  require 'expectation_matcher'
4
4
  require 'http_client'
5
+ require 'api_configuration'
5
6
 
6
7
  CONFIG_FILE = "config/api_runner.yml"
7
8
  SPEC_PATH = "test/api_runner/"
@@ -10,12 +11,13 @@ class ApiRunner
10
11
  # initializes the object, loads environment, build base_uri
11
12
  def initialize(env)
12
13
  @spec = []
13
- @errors = []
14
+ @results = []
14
15
  @excludes = []
16
+ @configuration = ApiConfiguration.new
15
17
  load_config(env)
16
18
  load_excludes(env)
17
19
  load_url_spec
18
- @http_client = HttpClient.new(@host, @port, @namespace)
20
+ @http_client = HttpClient.new(@configuration.host, @configuration.port, @configuration.namespace)
19
21
  @expectation = ExpectationMatcher.new(@excludes)
20
22
  end
21
23
 
@@ -23,11 +25,11 @@ class ApiRunner
23
25
  def run
24
26
  if server_is_available?
25
27
  run_tests
26
- @errors.each_with_index do |error, index|
27
- puts("\n\nError (#{index+1}): #{error}")
28
- end unless @errors.empty?
28
+ @results.each_with_index do |result, index|
29
+ result.honk_in(@configuration.verbosity, index)
30
+ end unless @results.empty?
29
31
  else
30
- puts("Server #{@host} seems to be unavailable!")
32
+ puts("Server #{@configuration.host} seems to be unavailable!")
31
33
  end
32
34
  end
33
35
 
@@ -38,13 +40,16 @@ class ApiRunner
38
40
  @spec.each do |test_case|
39
41
  response = send_request(test_case['request']['method'].downcase.to_sym, test_case['request']['path'], test_case['request']['body'])
40
42
  @expectation.test_types.each do |test_type|
41
- test = @expectation.check(test_type, response, test_case)
42
- if not test.succeeded
43
- @errors << test.error
43
+ result = @expectation.check(test_type, response, test_case)
44
+ if not result.succeeded
44
45
  putc "F"
46
+ @results << result
45
47
  break
46
48
  else
47
- putc "." if test_type == @expectation.test_types.last
49
+ if test_type == @expectation.test_types.last
50
+ @results << result
51
+ putc "."
52
+ end
48
53
  end
49
54
  end
50
55
  end
@@ -57,19 +62,20 @@ class ApiRunner
57
62
 
58
63
  # builds target uri from base uri generated of host port and namespace as well as the ressource path
59
64
  def target_uri
60
- "#{@protocol}://#{@host}"
65
+ "#{@configuration.protocol}://#{@configuration.host}"
61
66
  end
62
67
 
63
68
  # returns true if server is available
64
69
  def server_is_available?
65
70
  return true
66
- !@http_client.send_request(:get, "#{@protocol}://#{@host}:#{@port}", {:timeout => 5}).nil?
71
+ !@http_client.send_request(:get, "#{@configuration.protocol}://#{@configuration.host}:#{@configuration.port}", {:timeout => 5}).nil?
67
72
  end
68
73
 
69
74
  # loads environment config data from yaml file
70
75
  def load_config(env)
71
76
  config = YAML.load_file(self.class.config_file)
72
- config[env.to_s].each { |key, value| instance_variable_set("@#{key}", value) }
77
+ config[env.to_s].each { |key, value| @configuration.instance_variable_set("@#{key}", value) }
78
+ @configuration.verbosity = config['general']['verbosity'].first
73
79
  end
74
80
 
75
81
  # loads spec cases from yaml files
@@ -1,4 +1,5 @@
1
1
  class ExpectationMatcher
2
+ require 'result'
2
3
  require 'nokogiri'
3
4
  require 'JSON'
4
5
 
@@ -21,13 +22,12 @@ class ExpectationMatcher
21
22
 
22
23
  # matches the given response code
23
24
  def response_code(response, testcase)
24
- result_struct = Struct.new(:succeeded, :error)
25
- results = result_struct.new(:succeeded => true, :error => nil)
25
+ result = Result.new(testcase, response)
26
26
  if not testcase['response_expectation']['status_code'].to_s == response.code.to_s
27
- results.succeeded = false
28
- results.error = "testcase '#{testcase['name']}'\n expected response code --#{testcase['response_expectation']['status_code']}--\n got response code --#{response.code}--"
27
+ result.succeeded = false
28
+ result.error_message = " expected response code --#{testcase['response_expectation']['status_code']}--\n got response code --#{response.code}--"
29
29
  end
30
- results
30
+ result
31
31
  end
32
32
 
33
33
  # checks the format of the given data of JSON conformity
@@ -35,55 +35,51 @@ class ExpectationMatcher
35
35
  def response_body_format(response, testcase)
36
36
  result_struct = Struct.new(:succeeded, :error)
37
37
  results = result_struct.new(:succeeded => true, :error => nil)
38
+ result = Result.new(testcase, response)
38
39
  if not valid_json?(response.body)
39
- results.succeeded = false
40
- results.error = "testcase '#{testcase['name']}'\n expected valid JSON in body\n got --#{response.body[1..400]}--"
40
+ result.succeeded = false
41
+ result.error_message = "expected valid JSON in body\n got --#{response.body[1..400]}--"
41
42
  end
42
- results
43
+ result
43
44
  end
44
45
 
45
46
  # matches the given response header
46
47
  def response_headers(response, testcase)
47
- result_struct = Struct.new(:succeeded, :error)
48
- results = result_struct.new(:succeeded => true, :error => nil)
48
+ result = Result.new(testcase, response)
49
49
 
50
50
  testcase['response_expectation']['headers'].each_pair do |header_name, header_value|
51
51
  if is_regex?(header_value)
52
52
  if not (excluded?(header_name) or regex_matches?(header_value, response.headers[header_name]))
53
- results.succeeded = false
54
- results.error = "testcase '#{testcase['name']}'\n expected header identifier --#{header_name}-- to match regex --#{header_value}--\n got --#{response.headers[header_name]}--"
53
+ result.succeeded = false
54
+ result.error_message = " expected header identifier --#{header_name}-- to match regex --#{header_value}--\n got --#{response.headers[header_name]}--"
55
55
  end
56
56
  else
57
57
  if not (excluded?(header_name) or string_matches?(header_value, response.headers[header_name]))
58
- results.succeeded = false
59
- results.error = "testcase '#{testcase['name']}'\n expected header identifier --#{header_name}-- to match --#{header_value}--\n got --#{response.headers[header_name]}--"
58
+ result.succeeded = false
59
+ result.error_message = " expected header identifier --#{header_name}-- to match --#{header_value}--\n got --#{response.headers[header_name]}--"
60
60
  end
61
61
  end
62
62
  end unless (testcase['response_expectation']['headers'].nil? or testcase['response_expectation']['headers'].empty?)
63
- return results
63
+ return result
64
64
  end
65
65
 
66
66
  # matches the given attributes and values against the ones from the response body
67
67
  def response_body(response, testcase)
68
- puts("Testcase #{testcase['name']}\n")
69
- puts("got response --#{response.body}--\n\n")
70
- puts("exp response --#{testcase['response_expectation']['body']}--\n")
71
- puts("___________________________\n\n\n")
72
- result_struct = Struct.new(:succeeded, :error)
73
- results = result_struct.new(:succeeded => true, :error => nil)
68
+ result = Result.new(testcase, response)
74
69
 
75
70
  expected_body_hash = testcase['response_expectation']['body']
76
71
 
77
72
  # in case we have no body expectation we simply return success
78
- return results if expected_body_hash.nil?
73
+ return result if expected_body_hash.nil?
79
74
 
80
75
  # in case the response body is nil or damaged we return an error
81
76
  begin
82
77
  responded_body_hash = JSON.parse(response.body)
83
78
  rescue
84
- results.succeeded = false
85
- results.error = "testcase '#{testcase['name']}'\n expected response to have a body\n got raw body --#{response.body}-- which is nil or an unparseable hash"
86
- return results
79
+ result = Result.new(testcase, response)
80
+ result.success = false
81
+ result.error_message = " expected response to have a body\n got raw body --#{response.body}-- which is nil or an unparseable hash"
82
+ return result
87
83
  end
88
84
 
89
85
  # else we build trees from both body structures...
@@ -97,25 +93,25 @@ class ExpectationMatcher
97
93
 
98
94
  # return error if response body does not have the expected entry
99
95
  if response_node.nil?
100
- results.succeeded = false
101
- results.error = "testcase '#{testcase['name']}'\n expected body to have identifier --#{expectation_node.name}--\n got nil"
102
- return results
96
+ result.succeeded = false
97
+ result.error_message = " expected body to have identifier --#{expectation_node.name}--\n got nil"
98
+ return result
103
99
  end
104
100
 
105
101
  # last but not least try the regex or direct match and return errors in case of any
106
102
  if is_regex?(expectation_node.text)
107
103
  if not (excluded?(expectation_node.name) or regex_matches?(expectation_node.text, response_node.text))
108
- results.succeeded = false
109
- results.error = "testcase '#{testcase['name']}'\n expected body identifier --#{expectation_node.name}-- to match regex --#{expectation_node.text}--\n got --#{response_node.text}--"
104
+ result.succeeded = false
105
+ result.error_message = " expected body identifier --#{expectation_node.name}-- to match regex --#{expectation_node.text}--\n got --#{response_node.text}--"
110
106
  end
111
107
  else
112
108
  if not (excluded?(expectation_node.name) or string_matches?(expectation_node.text, response_node.text))
113
- results.succeeded = false
114
- results.error = "testcase '#{testcase['name']}'\n expected body identifier --#{expectation_node.name}-- to match --#{expectation_node.text}--\n got --#{response_node.text}--"
109
+ result.succeeded = false
110
+ result.error_message = " expected body identifier --#{expectation_node.name}-- to match --#{expectation_node.text}--\n got --#{response_node.text}--"
115
111
  end
116
112
  end
117
113
  end
118
- results
114
+ result
119
115
  end
120
116
 
121
117
  # recursively parses the tree and returns a set of relative pathes
data/lib/result.rb ADDED
@@ -0,0 +1,69 @@
1
+ class Result
2
+
3
+ attr_accessor :succeeded
4
+ attr_accessor :error_message
5
+
6
+ def initialize(testcase, response)
7
+ @succeeded = true
8
+ @testcase = testcase
9
+ @response = response
10
+ @error_message = nil
11
+ end
12
+
13
+ # honk out the errors message corresponding to the verbosity configuration the user took
14
+ def honk_in(verbosity="rspec", index)
15
+ self.send(verbosity.to_sym, index)
16
+ end
17
+
18
+ private
19
+
20
+ # yields out rspec like error messages only in case of an error
21
+ def rspec(index)
22
+ if not @succeeded
23
+ puts "\nError (#{index}) - \"#{@testcase['name']}\""
24
+ puts @error_message
25
+ end
26
+ end
27
+
28
+ # yields a more verbose error message only in case of an error
29
+ def verbose_on_error(index)
30
+ if not @succeeded
31
+ be_verbose(index)
32
+ end
33
+ end
34
+
35
+ # yields a more verbose message in case of an error AND success
36
+ def verbose_on_success(index)
37
+ be_verbose(index)
38
+ end
39
+
40
+ # yields a more verbose message in any case and includes a curl command to manually simulate the testcase
41
+ def verbose_with_curl(index)
42
+ be_verbose(index)
43
+ puts "\n simulate this call with: \"curl TODO\""
44
+ end
45
+
46
+ # yields the verbose error messages
47
+ def be_verbose(index)
48
+ puts "\n#{result_case} (#{index+1}) - \"#{@testcase['name']}\""
49
+ puts @error_message
50
+ puts (" More more more verbosity\n")
51
+ puts (" request method: #{@testcase['request']['method']}")
52
+ puts (" resource path: #{@testcase['request']['path']}")
53
+ puts (" request headers: #{@testcase['request']['headers']}")
54
+ puts (" JSON body sent: #{@testcase['request']['body']}")
55
+ puts (" response status code: #{@testcase['response_expectation']['status_code']}")
56
+ puts (" response headers: #{@testcase['response_expectation']['headers']}")
57
+ puts (" response body: #{@testcase['response_expectation']['body']}")
58
+ end
59
+
60
+ # returns the result case for interpolation in the output message header
61
+ def result_case
62
+ if @succeeded
63
+ "Success"
64
+ else
65
+ "Error"
66
+ end
67
+ end
68
+
69
+ end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 8
9
- version: 0.1.8
8
+ - 9
9
+ version: 0.1.9
10
10
  platform: ruby
11
11
  authors:
12
12
  - jan@moviepilot.com
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-09-23 00:00:00 +02:00
17
+ date: 2010-09-27 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -238,11 +238,13 @@ files:
238
238
  - features/apirunner.feature
239
239
  - features/step_definitions/apirunner_steps.rb
240
240
  - features/support/env.rb
241
+ - lib/api_configuration.rb
241
242
  - lib/api_runner.rb
242
243
  - lib/apirunner.rb
243
244
  - lib/apirunner/railtie.rb
244
245
  - lib/expectation_matcher.rb
245
246
  - lib/http_client.rb
247
+ - lib/result.rb
246
248
  - lib/tasks/api.rake
247
249
  - spec/.rspec
248
250
  - spec/api_runner_spec.rb
@@ -263,7 +265,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
263
265
  requirements:
264
266
  - - ">="
265
267
  - !ruby/object:Gem::Version
266
- hash: 1758241369738370513
268
+ hash: -2943715373765808099
267
269
  segments:
268
270
  - 0
269
271
  version: "0"