apirunner 0.4.10 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -7,7 +7,8 @@ source "http://rubygems.org"
7
7
  # Include everything needed to run rake, tests, features, etc.
8
8
 
9
9
  gem 'nokogiri', '~> 1.4.3.1'
10
- gem 'json'
10
+ gem 'json', '~> 1.4.6'
11
+ gem 'aaronh-chronic', '~> 0.3.9'
11
12
 
12
13
 
13
14
  group :development do
data/Gemfile.lock CHANGED
@@ -1,6 +1,7 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
+ aaronh-chronic (0.3.9)
4
5
  builder (2.1.2)
5
6
  cucumber (0.8.5)
6
7
  builder (~> 2.1.2)
@@ -40,10 +41,11 @@ PLATFORMS
40
41
  ruby
41
42
 
42
43
  DEPENDENCIES
44
+ aaronh-chronic (~> 0.3.9)
43
45
  bundler (~> 1.0.0)
44
46
  cucumber
45
47
  jeweler (~> 1.5.0.pre3)
46
- json
48
+ json (~> 1.4.6)
47
49
  mocha (>= 0.9.8)
48
50
  nokogiri (~> 1.4.3.1)
49
51
  rcov
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.10
1
+ 0.5.0
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.4.10"
8
+ s.version = "0.5.0"
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-10-29}
12
+ s.date = %q{2010-11-02}
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 = [
@@ -99,7 +99,8 @@ Gem::Specification.new do |s|
99
99
 
100
100
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
101
101
  s.add_runtime_dependency(%q<nokogiri>, ["~> 1.4.3.1"])
102
- s.add_runtime_dependency(%q<json>, [">= 0"])
102
+ s.add_runtime_dependency(%q<json>, ["~> 1.4.6"])
103
+ s.add_runtime_dependency(%q<aaronh-chronic>, ["~> 0.3.9"])
103
104
  s.add_development_dependency(%q<rspec>, [">= 2.0.0.beta.19"])
104
105
  s.add_development_dependency(%q<cucumber>, [">= 0"])
105
106
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
@@ -113,7 +114,8 @@ Gem::Specification.new do |s|
113
114
  s.add_development_dependency(%q<rcov>, [">= 0"])
114
115
  else
115
116
  s.add_dependency(%q<nokogiri>, ["~> 1.4.3.1"])
116
- s.add_dependency(%q<json>, [">= 0"])
117
+ s.add_dependency(%q<json>, ["~> 1.4.6"])
118
+ s.add_dependency(%q<aaronh-chronic>, ["~> 0.3.9"])
117
119
  s.add_dependency(%q<rspec>, [">= 2.0.0.beta.19"])
118
120
  s.add_dependency(%q<cucumber>, [">= 0"])
119
121
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
@@ -128,7 +130,8 @@ Gem::Specification.new do |s|
128
130
  end
129
131
  else
130
132
  s.add_dependency(%q<nokogiri>, ["~> 1.4.3.1"])
131
- s.add_dependency(%q<json>, [">= 0"])
133
+ s.add_dependency(%q<json>, ["~> 1.4.6"])
134
+ s.add_dependency(%q<aaronh-chronic>, ["~> 0.3.9"])
132
135
  s.add_dependency(%q<rspec>, [">= 2.0.0.beta.19"])
133
136
  s.add_dependency(%q<cucumber>, [">= 0"])
134
137
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
data/lib/checker.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'chronic'
2
+
1
3
  class Checker
2
4
  @@children = []
3
5
 
@@ -19,6 +21,17 @@ class Checker
19
21
  end
20
22
 
21
23
  private
24
+ def get_time(time)
25
+ one_day = 24 * 3600 # seconds
26
+ if time.match(/^@next_occurence_of/)
27
+ time = Chronic.parse( "next #{time.gsub(/^@next_occurence_of/, '')}" ) || Chronic.parse(time.gsub(/^@next_occurence_of/, ''))
28
+ time -= one_day if time - Time.now > one_day
29
+ else
30
+ time = Chronic.parse( time.gsub(/^@/,'') )
31
+ end
32
+
33
+ Chronic.parse(time)
34
+ end
22
35
 
23
36
  # tracks all children of this class
24
37
  # this way plugins can be loaded automagically
@@ -31,6 +44,26 @@ class Checker
31
44
  @excludes.include?(item)
32
45
  end
33
46
 
47
+ def is_time_check?(header, expectation)
48
+ return false unless header and expectation
49
+ # only support time check for certain headers + custom X-* headers
50
+ return false unless ["cache-control[max-age]", "cache-control[s-maxage]", "cache-control[min-fresh]", "retry-after", "last-modified"].include?(header.downcase) || header.downcase.match(/x-/)
51
+ return false unless expectation.strip.match(/^@/)
52
+ return true
53
+ end
54
+
55
+
56
+ def compare_time(header, expectation, value)
57
+ return false unless is_time_check?(header, expectation)
58
+ if ["cache-control[max-age]", "cache-control[s-maxage]", "cache-control[min-fresh]", "retry-after"].include?(header.downcase) || header.downcase.match(/x-/)
59
+ diff = get_time(expectation) - Time.now - value.to_i
60
+ elsif header.to_s.downcase == "last-modified"
61
+ diff = get_time(expectation) - Chronic.parse(value)
62
+ end
63
+
64
+ diff >= -5 && diff <= 5
65
+ end
66
+
34
67
  def is_number_comparison?(string)
35
68
  return false unless string
36
69
  string.match(/^[><]\s*\d+\s*$/) || string.match(/^[<>=]=\s*\d+\s*$/)
@@ -11,7 +11,7 @@ module CurlCommandGenerator
11
11
 
12
12
  def body2arg(body)
13
13
  # TODO: format body depending on the content type that is set, not always as json
14
- body ? "-d'#{body.to_json}'" : ""
14
+ body.nil? || body == {} || body == "" ? "" : "-d'#{body.to_json}'"
15
15
  end
16
16
 
17
17
  def headers2args(hash)
data/lib/http_client.rb CHANGED
@@ -30,7 +30,19 @@ class HttpClient
30
30
  response.code = raw_response.code
31
31
  response.message = raw_response.message
32
32
  response.body = raw_response.body
33
- response.headers = raw_response.to_hash.keys.inject({}){|hash, key| hash[key.to_s.downcase] = raw_response.to_hash[key][0]; hash}
33
+ response.headers = raw_response.to_hash.keys.inject({}){ |hash, key|
34
+ value = raw_response.to_hash[key][0]
35
+ hash[key.to_s.downcase] = value
36
+ if value =~ /=/
37
+ sub_values = value.tr(","," ").split(" ").select{|x| x =~ /=/}
38
+ sub_values.each do |sub_value|
39
+ s_key, s_value = sub_value.split("=")
40
+ hash["#{key}[#{s_key}]"] = s_value
41
+ end
42
+ end
43
+
44
+ hash
45
+ }
34
46
  response.runtime = runtime
35
47
  response.fully_qualified_path = (method == "GET" ? build_uri(resource, params).request_uri : resource_path(resource))
36
48
  response
@@ -11,6 +11,11 @@ class ResponseHeaderChecker < Checker
11
11
  result.succeeded = false
12
12
  result.error_message = " expected header identifier --#{header_name}-- to match regex --#{header_value}--\n got --#{@response.headers[header_name]}--"
13
13
  end
14
+ elsif is_time_check?(header_name, header_value)
15
+ if not (excluded?(header_name) or compare_time(header_name, header_value, @response.headers[header_name]))
16
+ result.succeeded = false
17
+ result.error_message = " expected header identifier --#{header_name}-- to match time (+/- 5 seconds) --#{header_value} / #{Chronic.parse(header_value.tr('@',''))}--\n got --#{@response.headers[header_name]}--"
18
+ end
14
19
  elsif is_number_comparison?(header_value)
15
20
  if not (excluded?(header_name) or compare_number(header_value, @response.headers[header_name]))
16
21
  result.succeeded = false
data/spec/checker_spec.rb CHANGED
@@ -71,6 +71,67 @@ describe "Checker" do
71
71
 
72
72
  end
73
73
 
74
+ describe "time_check" do
75
+ it "should perform a time check for max-age, s-maxage, min-fresh, retry-after, last-modified and X-* headers when expectation starts with @" do
76
+ ["cache-control[max-age]", "cache-control[s-maxage]", "cache-control[min-fresh]", "retry-after", "Last-Modified", "X-My-Custom-Timestamp"].each do |header|
77
+ Checker.new({}, {}).send(:is_time_check?, header, "@tomorrow 4:00am").should be_true
78
+ end
79
+ end
80
+
81
+ it "should not perform a time check for a header different to max-age, s-maxage, min-fresh, retry-after or X-* headers" do
82
+ %w{Server Content-Type Content-Length Via Connection}.each do |header|
83
+ Checker.new({}, {}).send(:is_time_check?, header, "@tomorrow 4:00am").should be_false
84
+ end
85
+ end
86
+
87
+ it "should not perform a time check for if header or expectations or both are nil" do
88
+ Checker.new({}, {}).send(:is_time_check?, nil, "@tomorrow 4:00am").should be_false
89
+ Checker.new({}, {}).send(:is_time_check?, "cache-control[max-age]", nil).should be_false
90
+ Checker.new({}, {}).send(:is_time_check?, nil, nil).should be_false
91
+ end
92
+
93
+ it "should correctly interpret @next_occurence_of " do
94
+ one_hour = 3600 # seconds
95
+ current_time = Chronic.parse("today #{Time.now.hour}:00")
96
+ three_hours_ago = current_time - 3*one_hour
97
+ in_three_hours = current_time + 3*one_hour
98
+
99
+ Checker.new({}, {}).send(:get_time, "@next_occurence_of #{three_hours_ago.hour}:00").should == three_hours_ago + 24 * one_hour
100
+ Checker.new({}, {}).send(:get_time, "@next_occurence_of #{in_three_hours.hour}:00").should == in_three_hours
101
+ end
102
+
103
+ it "should correctly compare delta-seconds for headers max-age, s-maxage, min-fresh, retry-after" do
104
+ in_five_hours = Chronic.parse("in 5 hours")
105
+ ["cache-control[max-age]", "cache-control[s-maxage]", "cache-control[min-fresh]", "retry-after", "X-My-Custom-Timestamp"].each do |header|
106
+ Checker.new({}, {}).send(:compare_time, header, "@next_occurence_of #{in_five_hours}", 5 * 3600).should be_true
107
+ end
108
+ end
109
+
110
+ it "should correctly transform an absolute time check to date for headers Last-Modified" do
111
+ in_five_hours = Chronic.parse("in 5 hours")
112
+ Checker.new({}, {}).send(:compare_time, "Last-Modified", "@#{in_five_hours}", in_five_hours.to_s).should be_true
113
+ end
114
+
115
+ def in_five_hours
116
+ Chronic.parse("in 5 hours")
117
+ end
118
+
119
+ it "should compare with +/- 5 seconds tolarance" do
120
+ #in_five_hours = Chronic.parse("in 5 hours")
121
+ (-4..4).each do |diff|
122
+ Checker.new({}, {}).send(:compare_time, "Last-Modified", "@#{in_five_hours}", Chronic.parse( (in_five_hours + diff) ).to_s).should be_true
123
+ ["cache-control[max-age]", "cache-control[s-maxage]", "cache-control[min-fresh]", "retry-after", "X-My-Custom-Timestamp"].each do |header|
124
+ Checker.new({}, {}).send(:compare_time, header, "@next_occurence_of #{in_five_hours}", 5 * 3600 + diff).should be_true
125
+ end
126
+ end
127
+
128
+ [-6,6].each do |diff|
129
+ Checker.new({}, {}).send(:compare_time, "Last-Modified", "@#{in_five_hours}", Chronic.parse( (in_five_hours + diff) ).to_s).should be_false
130
+ Checker.new({}, {}).send(:compare_time, "Last-Modified", "@next_occurence_of #{in_five_hours}", 5 * 3600 + diff).should be_false
131
+ end
132
+ end
133
+ end
134
+
74
135
  describe "compare_number" do
75
136
  it "should compare lt and lte correctly" do
76
137
  Checker.new({}, {}).send(:compare_number, "<4", "5").should be_false
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 4
8
- - 10
9
- version: 0.4.10
7
+ - 5
8
+ - 0
9
+ version: 0.5.0
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-10-29 00:00:00 +02:00
17
+ date: 2010-11-02 00:00:00 +01:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -38,17 +38,34 @@ dependencies:
38
38
  requirement: &id002 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
- - - ">="
41
+ - - ~>
42
42
  - !ruby/object:Gem::Version
43
43
  segments:
44
- - 0
45
- version: "0"
44
+ - 1
45
+ - 4
46
+ - 6
47
+ version: 1.4.6
46
48
  type: :runtime
47
49
  prerelease: false
48
50
  version_requirements: *id002
49
51
  - !ruby/object:Gem::Dependency
50
- name: rspec
52
+ name: aaronh-chronic
51
53
  requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ~>
57
+ - !ruby/object:Gem::Version
58
+ segments:
59
+ - 0
60
+ - 3
61
+ - 9
62
+ version: 0.3.9
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: *id003
66
+ - !ruby/object:Gem::Dependency
67
+ name: rspec
68
+ requirement: &id004 !ruby/object:Gem::Requirement
52
69
  none: false
53
70
  requirements:
54
71
  - - ">="
@@ -62,10 +79,10 @@ dependencies:
62
79
  version: 2.0.0.beta.19
63
80
  type: :development
64
81
  prerelease: false
65
- version_requirements: *id003
82
+ version_requirements: *id004
66
83
  - !ruby/object:Gem::Dependency
67
84
  name: cucumber
68
- requirement: &id004 !ruby/object:Gem::Requirement
85
+ requirement: &id005 !ruby/object:Gem::Requirement
69
86
  none: false
70
87
  requirements:
71
88
  - - ">="
@@ -75,10 +92,10 @@ dependencies:
75
92
  version: "0"
76
93
  type: :development
77
94
  prerelease: false
78
- version_requirements: *id004
95
+ version_requirements: *id005
79
96
  - !ruby/object:Gem::Dependency
80
97
  name: bundler
81
- requirement: &id005 !ruby/object:Gem::Requirement
98
+ requirement: &id006 !ruby/object:Gem::Requirement
82
99
  none: false
83
100
  requirements:
84
101
  - - ~>
@@ -90,10 +107,10 @@ dependencies:
90
107
  version: 1.0.0
91
108
  type: :development
92
109
  prerelease: false
93
- version_requirements: *id005
110
+ version_requirements: *id006
94
111
  - !ruby/object:Gem::Dependency
95
112
  name: jeweler
96
- requirement: &id006 !ruby/object:Gem::Requirement
113
+ requirement: &id007 !ruby/object:Gem::Requirement
97
114
  none: false
98
115
  requirements:
99
116
  - - ~>
@@ -106,10 +123,10 @@ dependencies:
106
123
  version: 1.5.0.pre3
107
124
  type: :development
108
125
  prerelease: false
109
- version_requirements: *id006
126
+ version_requirements: *id007
110
127
  - !ruby/object:Gem::Dependency
111
128
  name: rcov
112
- requirement: &id007 !ruby/object:Gem::Requirement
129
+ requirement: &id008 !ruby/object:Gem::Requirement
113
130
  none: false
114
131
  requirements:
115
132
  - - ">="
@@ -119,10 +136,10 @@ dependencies:
119
136
  version: "0"
120
137
  type: :development
121
138
  prerelease: false
122
- version_requirements: *id007
139
+ version_requirements: *id008
123
140
  - !ruby/object:Gem::Dependency
124
141
  name: mocha
125
- requirement: &id008 !ruby/object:Gem::Requirement
142
+ requirement: &id009 !ruby/object:Gem::Requirement
126
143
  none: false
127
144
  requirements:
128
145
  - - ">="
@@ -134,10 +151,10 @@ dependencies:
134
151
  version: 0.9.8
135
152
  type: :development
136
153
  prerelease: false
137
- version_requirements: *id008
154
+ version_requirements: *id009
138
155
  - !ruby/object:Gem::Dependency
139
156
  name: rspec
140
- requirement: &id009 !ruby/object:Gem::Requirement
157
+ requirement: &id010 !ruby/object:Gem::Requirement
141
158
  none: false
142
159
  requirements:
143
160
  - - ">="
@@ -151,10 +168,10 @@ dependencies:
151
168
  version: 2.0.0.beta.19
152
169
  type: :development
153
170
  prerelease: false
154
- version_requirements: *id009
171
+ version_requirements: *id010
155
172
  - !ruby/object:Gem::Dependency
156
173
  name: cucumber
157
- requirement: &id010 !ruby/object:Gem::Requirement
174
+ requirement: &id011 !ruby/object:Gem::Requirement
158
175
  none: false
159
176
  requirements:
160
177
  - - ">="
@@ -164,10 +181,10 @@ dependencies:
164
181
  version: "0"
165
182
  type: :development
166
183
  prerelease: false
167
- version_requirements: *id010
184
+ version_requirements: *id011
168
185
  - !ruby/object:Gem::Dependency
169
186
  name: bundler
170
- requirement: &id011 !ruby/object:Gem::Requirement
187
+ requirement: &id012 !ruby/object:Gem::Requirement
171
188
  none: false
172
189
  requirements:
173
190
  - - ~>
@@ -179,10 +196,10 @@ dependencies:
179
196
  version: 1.0.0
180
197
  type: :development
181
198
  prerelease: false
182
- version_requirements: *id011
199
+ version_requirements: *id012
183
200
  - !ruby/object:Gem::Dependency
184
201
  name: jeweler
185
- requirement: &id012 !ruby/object:Gem::Requirement
202
+ requirement: &id013 !ruby/object:Gem::Requirement
186
203
  none: false
187
204
  requirements:
188
205
  - - ~>
@@ -195,10 +212,10 @@ dependencies:
195
212
  version: 1.5.0.pre3
196
213
  type: :development
197
214
  prerelease: false
198
- version_requirements: *id012
215
+ version_requirements: *id013
199
216
  - !ruby/object:Gem::Dependency
200
217
  name: rcov
201
- requirement: &id013 !ruby/object:Gem::Requirement
218
+ requirement: &id014 !ruby/object:Gem::Requirement
202
219
  none: false
203
220
  requirements:
204
221
  - - ">="
@@ -208,7 +225,7 @@ dependencies:
208
225
  version: "0"
209
226
  type: :development
210
227
  prerelease: false
211
- version_requirements: *id013
228
+ version_requirements: *id014
212
229
  description: apirunner is a testsuite to query your RESTful JSON API and match response with your defined expectations
213
230
  email: developers@moviepilot.com
214
231
  executables: []
@@ -292,7 +309,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
292
309
  requirements:
293
310
  - - ">="
294
311
  - !ruby/object:Gem::Version
295
- hash: -2862160756938208368
312
+ hash: -4037813036394983070
296
313
  segments:
297
314
  - 0
298
315
  version: "0"