rest-assured 0.2.0.rc8 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +2 -2
- data/LICENSE +4 -19
- data/README.markdown +123 -36
- data/bin/console +1 -2
- data/bin/rest-assured +12 -0
- data/features/command_line_options.feature +20 -1
- data/features/{doubles_via_api.feature → rest_api/doubles.feature} +0 -0
- data/features/{redirect_rules_via_api.feature → rest_api/redirects.feature} +11 -8
- data/features/{call_history.feature → ruby_api/verify_requests.feature} +0 -0
- data/features/ruby_api/wait_for_requests.feature +39 -0
- data/features/step_definitions/command_line_options_steps.rb +12 -0
- data/features/step_definitions/doubles_steps.rb +10 -10
- data/features/step_definitions/redirect_rules_steps.rb +24 -5
- data/features/step_definitions/ruby_api_steps.rb +69 -0
- data/features/support/env.rb +3 -1
- data/features/{doubles_via_ui.feature → web_ui/doubles.feature} +0 -0
- data/features/{redirect_rules_via_ui.feature → web_ui/redirects.feature} +0 -0
- data/lib/rest-assured.rb +2 -1
- data/lib/rest-assured/client/resources.rb +12 -2
- data/lib/rest-assured/config.rb +24 -0
- data/lib/rest-assured/models/double.rb +32 -28
- data/lib/rest-assured/models/redirect.rb +28 -24
- data/lib/rest-assured/models/request.rb +11 -7
- data/lib/rest-assured/routes/double.rb +8 -8
- data/lib/rest-assured/routes/redirect.rb +8 -8
- data/lib/rest-assured/routes/response.rb +16 -14
- data/lib/rest-assured/version.rb +1 -1
- data/lib/sinatra/handler_options_patch.rb +25 -0
- data/spec/client/resource_double_spec.rb +42 -6
- data/spec/config_spec.rb +35 -0
- data/spec/functional/double_routes_spec.rb +109 -107
- data/spec/functional/redirect_routes_spec.rb +86 -75
- data/spec/functional/response_spec.rb +57 -55
- data/spec/models/double_spec.rb +67 -65
- data/spec/models/redirect_spec.rb +28 -26
- data/spec/models/request_spec.rb +10 -8
- data/ssl/localhost.crt +14 -0
- data/ssl/localhost.key +15 -0
- metadata +25 -24
- data/features/step_definitions/call_history_steps.rb +0 -24
@@ -1,16 +1,20 @@
|
|
1
1
|
Given /^there are no redirect rules$/ do
|
2
|
-
Redirect.destroy_all
|
2
|
+
RestAssured::Models::Redirect.destroy_all
|
3
3
|
end
|
4
4
|
|
5
5
|
Then /^I should get (\d+)$/ do |code|
|
6
6
|
last_response.status.should.to_s == code
|
7
7
|
end
|
8
8
|
|
9
|
-
|
10
|
-
post '/redirects', { :pattern => pattern, :to =>
|
9
|
+
Given /^there is redirect with pattern "([^"]*)" and uri "([^"]*)"$/ do |pattern, url|
|
10
|
+
post '/redirects', { :pattern => pattern, :to => url }
|
11
11
|
last_response.should be_ok
|
12
12
|
end
|
13
13
|
|
14
|
+
When /^I register redirect with pattern "([^"]*)" and uri "([^"]*)"$/ do |pattern, url|
|
15
|
+
Given %{there is redirect with pattern "#{pattern}" and uri "#{url}"}
|
16
|
+
end
|
17
|
+
|
14
18
|
Then /^it should redirect to "([^"]*)"$/ do |real_api_url|
|
15
19
|
follow_redirect!
|
16
20
|
last_response.header['Location'].should == real_api_url
|
@@ -18,7 +22,7 @@ end
|
|
18
22
|
|
19
23
|
Given /^the following redirects exist:$/ do |redirects|
|
20
24
|
redirects.hashes.each do |row|
|
21
|
-
Redirect.create(:pattern => row['pattern'], :to => row['to'])
|
25
|
+
RestAssured::Models::Redirect.create(:pattern => row['pattern'], :to => row['to'])
|
22
26
|
end
|
23
27
|
end
|
24
28
|
|
@@ -50,7 +54,7 @@ Given /^I choose to delete redirect with pattern "([^"]*)"$/ do |pattern|
|
|
50
54
|
end
|
51
55
|
|
52
56
|
When /^I reorder second redirect to be the first one$/ do
|
53
|
-
handler = find("#redirects #redirect_#{Redirect.last.id} td.handle")
|
57
|
+
handler = find("#redirects #redirect_#{RestAssured::Models::Redirect.last.id} td.handle")
|
54
58
|
target = find('#redirects thead')
|
55
59
|
|
56
60
|
handler.drag_to target
|
@@ -64,3 +68,18 @@ Then /^"([^"]*)" should be redirected to "([^"]*)"$/ do |missing_request, url|
|
|
64
68
|
|
65
69
|
last_request.url.should == "#{url}#{missing_request}"
|
66
70
|
end
|
71
|
+
|
72
|
+
Given /^blank slate$/ do
|
73
|
+
end
|
74
|
+
|
75
|
+
Given /^there are some redirects$/ do
|
76
|
+
RestAssured::Models::Redirect.create(:pattern => 'something', :to => 'somewhere')
|
77
|
+
end
|
78
|
+
|
79
|
+
When /^I delete all redirects$/ do
|
80
|
+
delete '/redirects/all'
|
81
|
+
end
|
82
|
+
|
83
|
+
Then /^there should be no redirects$/ do
|
84
|
+
RestAssured::Models::Redirect.count.should == 0
|
85
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
Given /^there is a double$/ do
|
2
|
+
@double = RestAssured::Double.create(:fullpath => '/some/path', :content => 'some content', :verb => 'POST')
|
3
|
+
end
|
4
|
+
|
5
|
+
When /^that double gets requested$/ do
|
6
|
+
post @double.fullpath, { :foo => 'bar' }.to_json, "CONTENT_TYPE" => "application/json"
|
7
|
+
post @double.fullpath, { :fooz => 'baaz'}, 'SOME_HEADER' => 'header_data'
|
8
|
+
end
|
9
|
+
|
10
|
+
When /^I request call history for that double$/ do
|
11
|
+
@requests = @double.reload.requests
|
12
|
+
end
|
13
|
+
|
14
|
+
Then /^I should see history records for those requests$/ do
|
15
|
+
@requests.first.body.should == { :foo => 'bar' }.to_json
|
16
|
+
JSON.parse( @requests.first.rack_env )["CONTENT_TYPE"].should == 'application/json'
|
17
|
+
|
18
|
+
JSON.parse( @requests.last.params ).should == { 'fooz' => 'baaz' }
|
19
|
+
JSON.parse( @requests.last.rack_env )["SOME_HEADER"].should == 'header_data'
|
20
|
+
end
|
21
|
+
|
22
|
+
Then /^it should be empty$/ do
|
23
|
+
@requests.size.should == 0
|
24
|
+
end
|
25
|
+
|
26
|
+
Given /^I created a double:$/ do |string|
|
27
|
+
# expected string is:
|
28
|
+
# @double = RestAssured::Double.create(:fullpath => '/some/api', :verb => 'POST')
|
29
|
+
eval string
|
30
|
+
end
|
31
|
+
|
32
|
+
When /^that double gets requested (#{CAPTURE_A_NUMBER}) times$/ do |num|
|
33
|
+
num.times do
|
34
|
+
sleep 0.5
|
35
|
+
send(@double.verb.downcase, @double.fullpath)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
When /^I wait for (\d+) requests:$/ do |num, string|
|
40
|
+
# expected string
|
41
|
+
# @double.wait_for_requests(3)
|
42
|
+
|
43
|
+
@wait_start = Time.now
|
44
|
+
@t = Thread.new do
|
45
|
+
begin
|
46
|
+
eval string
|
47
|
+
rescue RestAssured::MoreRequestsExpected => e
|
48
|
+
@more_reqs_exc = e
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
Then /^it should let me through$/ do
|
54
|
+
@t.join
|
55
|
+
@more_reqs_exc.should == nil
|
56
|
+
end
|
57
|
+
|
58
|
+
Then /^it should wait for (#{CAPTURE_A_NUMBER}) seconds(?: \(default timeout\))?$/ do |timeout|
|
59
|
+
@t.join
|
60
|
+
wait_time = Time.now - @wait_start
|
61
|
+
#(timeout..(timeout+1)).should cover(wait_time) # cover() only avilable in 1.9
|
62
|
+
wait_time.should >= timeout
|
63
|
+
wait_time.should < timeout + 1
|
64
|
+
end
|
65
|
+
|
66
|
+
Then /^it should raise MoreRequestsExpected error after with the following message:$/ do |string|
|
67
|
+
@more_reqs_exc.should be_instance_of RestAssured::MoreRequestsExpected
|
68
|
+
@more_reqs_exc.message.should =~ /#{string}/
|
69
|
+
end
|
data/features/support/env.rb
CHANGED
@@ -3,7 +3,7 @@ require 'rubygems'
|
|
3
3
|
require 'spork'
|
4
4
|
|
5
5
|
Spork.prefork do
|
6
|
-
require 'rspec
|
6
|
+
require 'rspec'
|
7
7
|
require 'rack/test'
|
8
8
|
require 'capybara'
|
9
9
|
require 'capybara/firebug'
|
@@ -77,6 +77,8 @@ Spork.each_run do
|
|
77
77
|
After do
|
78
78
|
sleep 0.1
|
79
79
|
DatabaseCleaner.clean
|
80
|
+
|
81
|
+
@t.join if @t.is_a?(Thread)
|
80
82
|
end
|
81
83
|
end
|
82
84
|
|
File without changes
|
File without changes
|
data/lib/rest-assured.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'sinatra/base'
|
3
|
+
require 'sinatra/handler_options_patch'
|
3
4
|
require 'haml'
|
4
5
|
require 'rack-flash'
|
5
6
|
require 'sinatra/partials'
|
@@ -14,7 +15,7 @@ require 'rest-assured/routes/response'
|
|
14
15
|
module RestAssured
|
15
16
|
class Application < Sinatra::Base
|
16
17
|
|
17
|
-
include
|
18
|
+
include Config
|
18
19
|
|
19
20
|
enable :method_override
|
20
21
|
|
@@ -1,8 +1,18 @@
|
|
1
1
|
require 'active_resource'
|
2
2
|
|
3
3
|
module RestAssured
|
4
|
-
|
5
|
-
|
4
|
+
class MoreRequestsExpected < StandardError; end
|
5
|
+
|
6
|
+
class Double < ActiveResource::Base
|
7
|
+
def wait_for_requests(n, opts = {})
|
8
|
+
timeout = opts[:timeout] || 5
|
9
|
+
|
10
|
+
timeout.times do
|
11
|
+
sleep 1
|
12
|
+
reload
|
13
|
+
return if requests.count >= n
|
14
|
+
end
|
15
|
+
raise MoreRequestsExpected.new("Expected #{n} requests. Got #{requests.count}.")
|
6
16
|
end
|
7
17
|
end
|
8
18
|
end
|
data/lib/rest-assured/config.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'logger'
|
2
2
|
require 'active_record'
|
3
3
|
require 'active_support/core_ext/kernel/reporting'
|
4
|
+
require 'webrick'
|
5
|
+
require 'webrick/https'
|
4
6
|
|
5
7
|
module RestAssured
|
6
8
|
module Config
|
@@ -37,11 +39,13 @@ module RestAssured
|
|
37
39
|
File.expand_path("../../../#{AppConfig.environment}.log", __FILE__)
|
38
40
|
end
|
39
41
|
build_db_config
|
42
|
+
build_ssl_config
|
40
43
|
end
|
41
44
|
|
42
45
|
def self.included(klass)
|
43
46
|
init_logger
|
44
47
|
setup_db
|
48
|
+
setup_ssl(klass) if AppConfig.use_ssl
|
45
49
|
|
46
50
|
klass.set :port, AppConfig.port
|
47
51
|
klass.set :environment, AppConfig.environment
|
@@ -52,6 +56,19 @@ module RestAssured
|
|
52
56
|
|
53
57
|
private
|
54
58
|
|
59
|
+
def self.setup_ssl(klass)
|
60
|
+
ssl_config = {
|
61
|
+
:SSLEnable => true,
|
62
|
+
:SSLCertificate => OpenSSL::X509::Certificate.new( File.read( AppConfig.ssl_cert ) ),
|
63
|
+
:SSLPrivateKey => OpenSSL::PKey::RSA.new( File.read( AppConfig.ssl_key ) ),
|
64
|
+
:SSLCertName => [ ["CN", WEBrick::Utils::getservername] ],
|
65
|
+
:SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE
|
66
|
+
}
|
67
|
+
|
68
|
+
klass.set :server, %[webrick]
|
69
|
+
klass.set :webrick, ssl_config
|
70
|
+
end
|
71
|
+
|
55
72
|
def self.setup_db
|
56
73
|
setup_db_logging
|
57
74
|
connect_db
|
@@ -118,6 +135,7 @@ module RestAssured
|
|
118
135
|
opts = {
|
119
136
|
:adapter => 'mysql',
|
120
137
|
:reconnect => true,
|
138
|
+
:pool => 20,
|
121
139
|
:user => AppConfig.user || 'root',
|
122
140
|
:database => AppConfig.database || default_database
|
123
141
|
}
|
@@ -132,6 +150,12 @@ module RestAssured
|
|
132
150
|
raise "Unsupported db adapter '#{AppConfig.adapter}'. Valid adapters are sqlite and mysql"
|
133
151
|
end
|
134
152
|
end
|
153
|
+
|
154
|
+
def self.build_ssl_config
|
155
|
+
AppConfig.use_ssl ||= false
|
156
|
+
AppConfig.ssl_cert ||= File.expand_path('../../../ssl/localhost.crt', __FILE__)
|
157
|
+
AppConfig.ssl_key ||= File.expand_path('../../../ssl/localhost.key', __FILE__)
|
158
|
+
end
|
135
159
|
end
|
136
160
|
end
|
137
161
|
|
@@ -1,43 +1,47 @@
|
|
1
1
|
require 'net/http'
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
module RestAssured
|
4
|
+
module Models
|
5
|
+
class Double < ActiveRecord::Base
|
6
|
+
attr_accessible :fullpath, :content, :description, :verb, :status
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
+
VERBS = %w{GET POST PUT DELETE}
|
9
|
+
STATUSES = Net::HTTPResponse::CODE_TO_OBJ.keys.map(&:to_i)
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
11
|
+
validates_presence_of :fullpath
|
12
|
+
validates_inclusion_of :verb, :in => VERBS
|
13
|
+
validates_inclusion_of :status, :in => STATUSES
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
before_save :toggle_active
|
16
|
+
before_validation :set_verb
|
17
|
+
before_validation :set_status
|
18
|
+
after_destroy :set_active
|
17
19
|
|
18
|
-
|
20
|
+
has_many :requests, :dependent => :destroy
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
22
|
+
private
|
23
|
+
def toggle_active
|
24
|
+
ne = id ? '!=' : 'IS NOT'
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
+
if active && Double.where("fullpath = ? AND active = ? AND id #{ne} ?", fullpath, true, id).exists?
|
27
|
+
Double.where("fullpath = ? AND id #{ne} ?", fullpath, id).update_all :active => false
|
28
|
+
end
|
26
29
|
end
|
27
|
-
end
|
28
30
|
|
29
|
-
|
30
|
-
|
31
|
-
|
31
|
+
def set_verb
|
32
|
+
self.verb = 'GET' unless verb.present?
|
33
|
+
end
|
32
34
|
|
33
|
-
|
34
|
-
|
35
|
-
|
35
|
+
def set_status
|
36
|
+
self.status = 200 unless status.present?
|
37
|
+
end
|
36
38
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
39
|
+
def set_active
|
40
|
+
if active && f = Double.where(:fullpath => fullpath).last
|
41
|
+
f.active = true
|
42
|
+
f.save
|
43
|
+
end
|
41
44
|
end
|
42
45
|
end
|
46
|
+
end
|
43
47
|
end
|
@@ -1,35 +1,39 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module RestAssured
|
2
|
+
module Models
|
3
|
+
class Redirect < ActiveRecord::Base
|
4
|
+
attr_accessible :pattern, :to, :position
|
3
5
|
|
4
|
-
|
6
|
+
validates_presence_of :pattern, :to
|
5
7
|
|
6
|
-
|
8
|
+
scope :ordered, order('position')
|
7
9
|
|
8
|
-
|
10
|
+
before_create :assign_position
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
+
def self.update_order(ordered_redirect_ids)
|
13
|
+
success = true
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
transaction do
|
16
|
+
begin
|
17
|
+
ordered_redirect_ids.each_with_index do |r_id, idx|
|
18
|
+
r = find(r_id)
|
19
|
+
r.position = idx
|
20
|
+
r.save!
|
21
|
+
end
|
22
|
+
rescue => e
|
23
|
+
# TODO log exception
|
24
|
+
puts e.inspect
|
25
|
+
success = false
|
26
|
+
raise ActiveRecord::Rollback
|
27
|
+
end
|
19
28
|
end
|
20
|
-
|
21
|
-
# TODO log exception
|
22
|
-
puts e.inspect
|
23
|
-
success = false
|
24
|
-
raise ActiveRecord::Rollback
|
29
|
+
success
|
25
30
|
end
|
26
|
-
end
|
27
|
-
success
|
28
|
-
end
|
29
31
|
|
30
|
-
|
32
|
+
private
|
31
33
|
|
32
|
-
|
33
|
-
|
34
|
+
def assign_position
|
35
|
+
self.position = ( self.class.maximum(:position) || -1 ) + 1
|
36
|
+
end
|
34
37
|
end
|
38
|
+
end
|
35
39
|
end
|
@@ -1,12 +1,16 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module RestAssured
|
2
|
+
module Models
|
3
|
+
class Request < ActiveRecord::Base
|
4
|
+
belongs_to :double
|
3
5
|
|
4
|
-
|
6
|
+
validates_presence_of :rack_env
|
5
7
|
|
6
|
-
|
8
|
+
after_create :save_created_at
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
|
10
|
+
private
|
11
|
+
def save_created_at
|
12
|
+
self.created_at = Time.now
|
13
|
+
end
|
11
14
|
end
|
15
|
+
end
|
12
16
|
end
|
@@ -8,18 +8,18 @@ module RestAssured
|
|
8
8
|
end
|
9
9
|
|
10
10
|
router.get '/doubles' do
|
11
|
-
@doubles = Double.all
|
11
|
+
@doubles = Models::Double.all
|
12
12
|
haml :'doubles/index'
|
13
13
|
end
|
14
14
|
|
15
15
|
router.get '/doubles/new' do
|
16
|
-
@double = Double.new
|
16
|
+
@double = Models::Double.new
|
17
17
|
haml :'doubles/new'
|
18
18
|
end
|
19
19
|
|
20
20
|
router.get '/doubles/:id.json' do |id|
|
21
21
|
begin
|
22
|
-
double = Double.find(id)
|
22
|
+
double = Models::Double.find(id)
|
23
23
|
body double.to_json(:include => :requests)
|
24
24
|
rescue ActiveRecord::RecordNotFound
|
25
25
|
status 404
|
@@ -29,7 +29,7 @@ module RestAssured
|
|
29
29
|
router.post /^\/doubles(\.json)?$/ do |passes_json|
|
30
30
|
f = { :fullpath => params['fullpath'], :content => params['content'], :description => params['description'], :verb => params['verb'], :status => params['status'] }
|
31
31
|
|
32
|
-
@double = Double.create(passes_json ? JSON.parse(request.body.read)['double'] : ( params['double'] || f ))
|
32
|
+
@double = Models::Double.create(passes_json ? JSON.parse(request.body.read)['double'] : ( params['double'] || f ))
|
33
33
|
|
34
34
|
if browser?
|
35
35
|
if @double.errors.blank?
|
@@ -50,12 +50,12 @@ module RestAssured
|
|
50
50
|
end
|
51
51
|
|
52
52
|
router.get %r{/doubles/(\d+)/edit} do |id|
|
53
|
-
@double = Double.find(id)
|
53
|
+
@double = Models::Double.find(id)
|
54
54
|
haml :'doubles/edit'
|
55
55
|
end
|
56
56
|
|
57
57
|
router.put %r{/doubles/(\d+)} do |id|
|
58
|
-
@double = Double.find(id)
|
58
|
+
@double = Models::Double.find(id)
|
59
59
|
|
60
60
|
if request.xhr?
|
61
61
|
if params['active']
|
@@ -77,14 +77,14 @@ module RestAssured
|
|
77
77
|
end
|
78
78
|
|
79
79
|
router.delete %r{/doubles/(\d+)} do |id|
|
80
|
-
if Double.destroy(id)
|
80
|
+
if Models::Double.destroy(id)
|
81
81
|
flash[:notice] = 'Double deleted'
|
82
82
|
redirect '/doubles'
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
86
|
router.delete '/doubles/all' do
|
87
|
-
status Double.delete_all ? 200 : 500
|
87
|
+
status Models::Double.delete_all ? 200 : 500
|
88
88
|
end
|
89
89
|
end
|
90
90
|
end
|