apirunner 0.4.10 → 0.5.0

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/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"