apirunner 0.1.5 → 0.1.6
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 +2 -1
- data/Gemfile.lock +4 -4
- data/README.rdoc +51 -0
- data/VERSION +1 -1
- data/apirunner.gemspec +5 -5
- data/examples/test/api_runner/001_create_ressource.yml +102 -2
- data/lib/api_runner.rb +6 -5
- data/lib/expectation_matcher.rb +5 -1
- data/lib/http_client.rb +61 -1
- data/lib/tasks/api.rake +5 -3
- metadata +8 -8
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 'httparty', '~> 0.6.1'
|
10
|
+
#gem 'httparty', '~> 0.6.1'
|
11
|
+
gem 'rest-client', '>= 1.6.1'
|
11
12
|
|
12
13
|
group :development do
|
13
14
|
gem "rspec", ">= 2.0.0.beta.19"
|
data/Gemfile.lock
CHANGED
@@ -2,7 +2,6 @@ GEM
|
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
4
|
builder (2.1.2)
|
5
|
-
crack (0.1.8)
|
6
5
|
cucumber (0.8.5)
|
7
6
|
builder (~> 2.1.2)
|
8
7
|
diff-lcs (~> 1.1.2)
|
@@ -13,18 +12,19 @@ GEM
|
|
13
12
|
gherkin (2.1.5)
|
14
13
|
trollop (~> 1.16.2)
|
15
14
|
git (1.2.5)
|
16
|
-
httparty (0.6.1)
|
17
|
-
crack (= 0.1.8)
|
18
15
|
jeweler (1.5.0.pre3)
|
19
16
|
bundler (~> 1.0.0)
|
20
17
|
git (>= 1.2.5)
|
21
18
|
rake
|
22
19
|
json_pure (1.4.6)
|
20
|
+
mime-types (1.16)
|
23
21
|
mocha (0.9.8)
|
24
22
|
rake
|
25
23
|
nokogiri (1.4.3.1)
|
26
24
|
rake (0.8.7)
|
27
25
|
rcov (0.9.9)
|
26
|
+
rest-client (1.6.1)
|
27
|
+
mime-types (>= 1.16)
|
28
28
|
rspec (2.0.0.beta.22)
|
29
29
|
rspec-core (= 2.0.0.beta.22)
|
30
30
|
rspec-expectations (= 2.0.0.beta.22)
|
@@ -44,9 +44,9 @@ PLATFORMS
|
|
44
44
|
DEPENDENCIES
|
45
45
|
bundler (~> 1.0.0)
|
46
46
|
cucumber
|
47
|
-
httparty (~> 0.6.1)
|
48
47
|
jeweler (~> 1.5.0.pre3)
|
49
48
|
mocha (>= 0.9.8)
|
50
49
|
nokogiri (~> 1.4.3.1)
|
51
50
|
rcov
|
51
|
+
rest-client (>= 1.6.1)
|
52
52
|
rspec (>= 2.0.0.beta.19)
|
data/README.rdoc
CHANGED
@@ -26,6 +26,7 @@ apirunner was initially developed for testing of the mighty (m8ty) i18n recommen
|
|
26
26
|
* print out a nice error report (that you as a awesome ruby coder will never see)
|
27
27
|
* be invoked from within rake to generate some example configuration and testcase files
|
28
28
|
* be invoked also from within rake to run your test's
|
29
|
+
* not travel to Ibiza
|
29
30
|
|
30
31
|
== Installation
|
31
32
|
|
@@ -45,6 +46,44 @@ Additionally there will be some example testcases which can be found in:
|
|
45
46
|
test/apirunner/002_delete_ressource.yml
|
46
47
|
test/apirunner/excludes.yml
|
47
48
|
|
49
|
+
At first take some time and change config/api_runner.yml to your needs. You might for example want to test your app locally on localhost:3000, on staging machine and on production environment too. So your api_runner.yml could look like that:
|
50
|
+
|
51
|
+
local:
|
52
|
+
protocol: http
|
53
|
+
host: localhost
|
54
|
+
port: 3000
|
55
|
+
namespace: api
|
56
|
+
staging:
|
57
|
+
protocol: http
|
58
|
+
host: staging.yourstagingdomain.dom
|
59
|
+
port: 80
|
60
|
+
namespace: api
|
61
|
+
production:
|
62
|
+
protocol: https
|
63
|
+
host: www.yourproductiondomain.dom
|
64
|
+
port: 80
|
65
|
+
namespace: prod_api
|
66
|
+
|
67
|
+
Take a look at "namespace" here. It makes the expectation matcher build ressource URI's like so:
|
68
|
+
|
69
|
+
http://localhost:3000/api
|
70
|
+
http://staging.yourstagingdomain.dom/api
|
71
|
+
http://www.yourproductiondomain.dom/prod_api
|
72
|
+
|
73
|
+
The ressource pathes are simply appended before the request is sent.
|
74
|
+
|
75
|
+
The file also generates your rake tasks dynamically. The above config will generate 3 new tasks:
|
76
|
+
|
77
|
+
api:run:local
|
78
|
+
api:run:staging
|
79
|
+
api:run:production
|
80
|
+
|
81
|
+
== Excludes
|
82
|
+
|
83
|
+
You may also want to define some excludes for some of your environment. Imageine on your localhost's server there is no "Last-Modified" present in the header, but you would like to check that on staging and production boxes.
|
84
|
+
|
85
|
+
Simply define your story to check "Last-Modified" generally and exclude it for
|
86
|
+
|
48
87
|
== Invocation
|
49
88
|
|
50
89
|
Assuming you defined an environment "local" and "staging" you can invoke your masterpiece with:
|
@@ -68,6 +107,18 @@ apirunner heavily depends on the following great GEM's:
|
|
68
107
|
1) nokogiri
|
69
108
|
2) httparty
|
70
109
|
|
110
|
+
== Examples
|
111
|
+
|
112
|
+
After invoking:
|
113
|
+
|
114
|
+
rake api:scaffold
|
115
|
+
|
116
|
+
you will find some YAML example files for request and expectation generation in test/api_runner. You can create as many story files here as you like, they are executed in the order they are read from the filesystem, so you should name them like 000_create_some_ressource.yml, 001_read_some_ressource.yml and so on.
|
117
|
+
|
118
|
+
Alternatively you can place all your stories into one single file.
|
119
|
+
|
120
|
+
Addition
|
121
|
+
|
71
122
|
== Authors
|
72
123
|
|
73
124
|
apirunner was written by:
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.6
|
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
|
+
s.version = "0.1.6"
|
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-
|
12
|
+
s.date = %q{2010-09-23}
|
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 = [
|
@@ -63,7 +63,7 @@ Gem::Specification.new do |s|
|
|
63
63
|
|
64
64
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
65
65
|
s.add_runtime_dependency(%q<nokogiri>, ["~> 1.4.3.1"])
|
66
|
-
s.add_runtime_dependency(%q<
|
66
|
+
s.add_runtime_dependency(%q<rest-client>, [">= 1.6.1"])
|
67
67
|
s.add_development_dependency(%q<rspec>, [">= 2.0.0.beta.19"])
|
68
68
|
s.add_development_dependency(%q<cucumber>, [">= 0"])
|
69
69
|
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
@@ -77,7 +77,7 @@ Gem::Specification.new do |s|
|
|
77
77
|
s.add_development_dependency(%q<rcov>, [">= 0"])
|
78
78
|
else
|
79
79
|
s.add_dependency(%q<nokogiri>, ["~> 1.4.3.1"])
|
80
|
-
s.add_dependency(%q<
|
80
|
+
s.add_dependency(%q<rest-client>, [">= 1.6.1"])
|
81
81
|
s.add_dependency(%q<rspec>, [">= 2.0.0.beta.19"])
|
82
82
|
s.add_dependency(%q<cucumber>, [">= 0"])
|
83
83
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
@@ -92,7 +92,7 @@ Gem::Specification.new do |s|
|
|
92
92
|
end
|
93
93
|
else
|
94
94
|
s.add_dependency(%q<nokogiri>, ["~> 1.4.3.1"])
|
95
|
-
s.add_dependency(%q<
|
95
|
+
s.add_dependency(%q<rest-client>, [">= 1.6.1"])
|
96
96
|
s.add_dependency(%q<rspec>, [">= 2.0.0.beta.19"])
|
97
97
|
s.add_dependency(%q<cucumber>, [">= 0"])
|
98
98
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
@@ -5,13 +5,42 @@
|
|
5
5
|
method: 'PUT'
|
6
6
|
body:
|
7
7
|
username: 'duffyduck'
|
8
|
+
watchlist:
|
9
|
+
- m333
|
10
|
+
- m79
|
11
|
+
blacklist:
|
12
|
+
- m334
|
13
|
+
- m77
|
14
|
+
skiplist:
|
15
|
+
- m335
|
16
|
+
- m78
|
17
|
+
ratings:
|
18
|
+
m336: 4
|
19
|
+
m79: 2.5
|
20
|
+
m777: 3.0
|
21
|
+
m567: 4.0
|
22
|
+
m354: 5.0
|
23
|
+
expires_at: 2011-09-09T22:41:50+00:00
|
8
24
|
response_expectation:
|
9
25
|
status_code: 201
|
10
26
|
headers:
|
11
27
|
Last-Modified: /.*/
|
12
28
|
body:
|
13
29
|
username: 'duffyduck'
|
14
|
-
|
30
|
+
watchlist:
|
31
|
+
- m333
|
32
|
+
- m79
|
33
|
+
blacklist:
|
34
|
+
- m334
|
35
|
+
- m77
|
36
|
+
skiplist:
|
37
|
+
- m335
|
38
|
+
- m78
|
39
|
+
ratings:
|
40
|
+
m336: 4.0
|
41
|
+
m79: 2.5
|
42
|
+
fsk: "18"
|
43
|
+
expires_at: Fri, 09 Sep 2011 22:41:50 +0000
|
15
44
|
- name: 'Update existing User - Update watchlist'
|
16
45
|
request:
|
17
46
|
path: '/users/duffyduck/watchlist'
|
@@ -22,4 +51,75 @@
|
|
22
51
|
response_expectation:
|
23
52
|
status_code: 204
|
24
53
|
body:
|
25
|
-
|
54
|
+
- name: 'Check User FSK,Watchlist'
|
55
|
+
request:
|
56
|
+
path: '/users/duffyduck'
|
57
|
+
method: 'GET'
|
58
|
+
response_expectation:
|
59
|
+
status_code: 200
|
60
|
+
headers:
|
61
|
+
Last-Modified: /.*/
|
62
|
+
body:
|
63
|
+
username: 'duffyduck'
|
64
|
+
fsk: "18"
|
65
|
+
watchlist:
|
66
|
+
- m367
|
67
|
+
- m73
|
68
|
+
blacklist:
|
69
|
+
- m334
|
70
|
+
- m77
|
71
|
+
skiplist:
|
72
|
+
- m335
|
73
|
+
- m78
|
74
|
+
ratings:
|
75
|
+
m336: 4.0
|
76
|
+
m79: 2.5
|
77
|
+
- name: 'Set 10 Ratings'
|
78
|
+
request:
|
79
|
+
path: '/users/duffyduck/ratings'
|
80
|
+
method: 'PUT'
|
81
|
+
body:
|
82
|
+
"m1035": 4
|
83
|
+
"m2087": 3
|
84
|
+
"m1554": 2
|
85
|
+
"m2981": 1
|
86
|
+
"m1590": 2
|
87
|
+
"m1056": 3
|
88
|
+
"m12493": 4
|
89
|
+
"m1875": 5
|
90
|
+
"m7258": 2.5
|
91
|
+
"m7339": 3.5
|
92
|
+
response_expectation:
|
93
|
+
status_code: 204
|
94
|
+
headers:
|
95
|
+
Last-Modified: /.*/
|
96
|
+
- name: 'Check User Ratings Update'
|
97
|
+
request:
|
98
|
+
path: '/users/duffyduck'
|
99
|
+
method: 'GET'
|
100
|
+
response_expectation:
|
101
|
+
status_code: 200
|
102
|
+
headers:
|
103
|
+
Last-Modified: /.*/
|
104
|
+
body:
|
105
|
+
username: 'duffyduck'
|
106
|
+
fsk: "18"
|
107
|
+
watchlist:
|
108
|
+
- m367
|
109
|
+
- m73
|
110
|
+
blacklist:
|
111
|
+
- m334
|
112
|
+
- m77
|
113
|
+
skiplist:
|
114
|
+
- m335
|
115
|
+
- m78
|
116
|
+
ratings:
|
117
|
+
"m1035": "1.0"
|
118
|
+
"m2087": "3.0"
|
119
|
+
"m1554": "2.0"
|
120
|
+
"m1590": "2.0"
|
121
|
+
"m1056": "3.0"
|
122
|
+
"m12493": "4.0"
|
123
|
+
"m1875": "5.0"
|
124
|
+
"m7258": "2.5"
|
125
|
+
"m7339": "3.5"
|
data/lib/api_runner.rb
CHANGED
@@ -10,13 +10,13 @@ class ApiRunner
|
|
10
10
|
|
11
11
|
# initializes the object, loads environment, build base_uri
|
12
12
|
def initialize(env)
|
13
|
-
@http_client = HttpClient.new
|
14
13
|
@spec = []
|
15
14
|
@errors = []
|
16
15
|
@excludes = []
|
17
16
|
load_config(env)
|
18
17
|
load_excludes(env)
|
19
18
|
load_url_spec
|
19
|
+
@http_client = HttpClient.new(@host, @port, @namespace)
|
20
20
|
@expectation = ExpectationMatcher.new(@excludes)
|
21
21
|
end
|
22
22
|
|
@@ -37,7 +37,7 @@ class ApiRunner
|
|
37
37
|
# runs all testcases that are provided by the testclass an fills errors if there are any
|
38
38
|
def run_tests
|
39
39
|
@spec.each do |test_case|
|
40
|
-
response = send_request(test_case['request']['method'].downcase.to_sym,
|
40
|
+
response = send_request(test_case['request']['method'].downcase.to_sym, test_case['request']['path'], test_case['request']['body'])
|
41
41
|
@expectation.test_types.each do |test_type|
|
42
42
|
test = @expectation.check(test_type, response, test_case)
|
43
43
|
if not test.succeeded
|
@@ -57,12 +57,13 @@ class ApiRunner
|
|
57
57
|
end
|
58
58
|
|
59
59
|
# builds target uri from base uri generated of host port and namespace as well as the ressource path
|
60
|
-
def target_uri
|
61
|
-
"#{@protocol}://#{@host}
|
60
|
+
def target_uri
|
61
|
+
"#{@protocol}://#{@host}"
|
62
62
|
end
|
63
63
|
|
64
64
|
# returns true if server is available
|
65
65
|
def server_is_available?
|
66
|
+
return true
|
66
67
|
!@http_client.send_request(:get, "#{@protocol}://#{@host}:#{@port}", {:timeout => 5}).nil?
|
67
68
|
end
|
68
69
|
|
@@ -83,7 +84,7 @@ class ApiRunner
|
|
83
84
|
# loads and parses items that need to be excluded from the checks in certain environments
|
84
85
|
def load_excludes(env)
|
85
86
|
excludes_file = self.class.excludes_file
|
86
|
-
@excludes = YAML.load_file(excludes_file).detect{ |a| a.first == env.to_s }[1]["excludes"]
|
87
|
+
@excludes = YAML.load_file(excludes_file).detect{ |a| a.first == env.to_s }[1]["excludes"] rescue nil
|
87
88
|
end
|
88
89
|
|
89
90
|
# returns config files path and can be stubbed this way
|
data/lib/expectation_matcher.rb
CHANGED
@@ -22,7 +22,7 @@ class ExpectationMatcher
|
|
22
22
|
def response_code(response, testcase)
|
23
23
|
result_struct = Struct.new(:succeeded, :error)
|
24
24
|
results = result_struct.new(:succeeded => true, :error => nil)
|
25
|
-
if not testcase['response_expectation']['status_code'] == response.code
|
25
|
+
if not testcase['response_expectation']['status_code'].to_s == response.code.to_s
|
26
26
|
results.succeeded = false
|
27
27
|
results.error = "testcase '#{testcase['name']}'\n expected response code --#{testcase['response_expectation']['status_code']}--\n got response code --#{response.code}--"
|
28
28
|
end
|
@@ -64,6 +64,10 @@ class ExpectationMatcher
|
|
64
64
|
|
65
65
|
# matches the given attributes and values against the ones from the response body
|
66
66
|
def response_body(response, testcase)
|
67
|
+
puts("Testcase #{testcase['name']}\n")
|
68
|
+
puts("got response --#{response.body}--\n\n")
|
69
|
+
puts("exp response --#{testcase['response_expectation']['body']}--\n")
|
70
|
+
puts("___________________________\n\n\n")
|
67
71
|
result_struct = Struct.new(:succeeded, :error)
|
68
72
|
results = result_struct.new(:succeeded => true, :error => nil)
|
69
73
|
|
data/lib/http_client.rb
CHANGED
@@ -1,10 +1,70 @@
|
|
1
1
|
class HttpClient
|
2
|
+
require 'net/http'
|
3
|
+
require 'JSON'
|
4
|
+
|
5
|
+
def initialize(host, port, namespace)
|
6
|
+
@http = Net::HTTP.new(host, port)
|
7
|
+
@host = host
|
8
|
+
@port = port
|
9
|
+
@namespace = namespace
|
10
|
+
end
|
11
|
+
|
12
|
+
def send_request(method, resource, data=nil)
|
13
|
+
build_response(self.send(method.to_s.downcase, resource, data))
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
# returns struct containing response.code, headers, body and message
|
19
|
+
# this is only for easily interfaceing another http client
|
20
|
+
def build_response(raw_response)
|
21
|
+
response_struct = Struct.new(:code, :message, :headers, :body)
|
22
|
+
response = response_struct.new
|
23
|
+
response.code = raw_response.code
|
24
|
+
response.message = raw_response.message
|
25
|
+
response.body = raw_response.body
|
26
|
+
response.headers = raw_response.header
|
27
|
+
response
|
28
|
+
end
|
29
|
+
|
30
|
+
def get(resource, params)
|
31
|
+
request = Net::HTTP::Get.new(resource_path(resource), initheader = {'Content-Type' =>'application/json'})
|
32
|
+
response = @http.request(request)
|
33
|
+
return response
|
34
|
+
end
|
35
|
+
|
36
|
+
def put(resource, data)
|
37
|
+
request = Net::HTTP::Put.new(resource_path(resource), initheader = {'Content-Type' =>'application/json'})
|
38
|
+
request.body = data.to_json
|
39
|
+
response = @http.request(request)
|
40
|
+
end
|
41
|
+
|
42
|
+
def post(resource, data)
|
43
|
+
request = Net::HTTP::Post.new(resource_path(resource), initheader = {'Content-Type' =>'application/json'})
|
44
|
+
request.body = data.to_json
|
45
|
+
response = @http.request(request)
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
def delete(resource, params)
|
50
|
+
request = Net::HTTP::Delete.new(resource_path(resource), initheader = {'Content-Type' =>'application/json'})
|
51
|
+
response = @http.request(request)
|
52
|
+
end
|
53
|
+
|
54
|
+
def resource_path(resource)
|
55
|
+
"/" + @namespace + resource
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
class HttPartyClient
|
2
61
|
require 'httparty'
|
3
62
|
include HTTParty
|
4
63
|
|
5
64
|
# sends http request with given method, uri and data and returns servers response
|
6
65
|
def send_request(method, uri, data=nil)
|
7
|
-
|
66
|
+
options = { :body => data.to_json, :format => :json }
|
67
|
+
build_response(self.class.send(method, uri, options))
|
8
68
|
end
|
9
69
|
|
10
70
|
protected
|
data/lib/tasks/api.rake
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
begin
|
2
|
+
config = YAML.load_file("#{Rails.root}/config/api_runner.yml")
|
3
|
+
rescue
|
4
|
+
end
|
3
5
|
namespace :api do
|
4
6
|
namespace :run do
|
5
7
|
config.each_key do |env|
|
@@ -10,7 +12,7 @@ namespace :api do
|
|
10
12
|
api_runner.run
|
11
13
|
puts "\nTestrun finished\n\n"
|
12
14
|
end
|
13
|
-
end
|
15
|
+
end unless config.nil?
|
14
16
|
end
|
15
17
|
desc "generates configuration and a skeleton for apirunner tests as well as excludes"
|
16
18
|
task :scaffold do
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 6
|
9
|
+
version: 0.1.6
|
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-
|
17
|
+
date: 2010-09-23 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -34,17 +34,17 @@ dependencies:
|
|
34
34
|
prerelease: false
|
35
35
|
version_requirements: *id001
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
|
-
name:
|
37
|
+
name: rest-client
|
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
|
-
-
|
44
|
+
- 1
|
45
45
|
- 6
|
46
46
|
- 1
|
47
|
-
version:
|
47
|
+
version: 1.6.1
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: *id002
|
@@ -263,7 +263,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
263
263
|
requirements:
|
264
264
|
- - ">="
|
265
265
|
- !ruby/object:Gem::Version
|
266
|
-
hash: -
|
266
|
+
hash: -4500206872193641389
|
267
267
|
segments:
|
268
268
|
- 0
|
269
269
|
version: "0"
|