rest-assured 0.3.2 → 1.0.0.rc1

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.
Files changed (48) hide show
  1. data/.rspec +1 -0
  2. data/Gemfile +4 -1
  3. data/README.markdown +47 -25
  4. data/bin/rest-assured +3 -3
  5. data/features/command_line_options.feature +2 -2
  6. data/features/rest_api/redirects.feature +10 -9
  7. data/features/ruby_api/create_double.feature +1 -6
  8. data/features/ruby_api/test_server.feature +42 -0
  9. data/features/ruby_api/verify_requests.feature +1 -6
  10. data/features/ruby_api/wait_for_requests.feature +2 -5
  11. data/features/step_definitions/command_line_options_steps.rb +1 -1
  12. data/features/step_definitions/doubles_steps.rb +4 -0
  13. data/features/step_definitions/redirect_rules_steps.rb +2 -6
  14. data/features/step_definitions/ruby_api_steps.rb +45 -0
  15. data/features/support/env.rb +9 -14
  16. data/features/support/world_helpers.rb +1 -1
  17. data/lib/rest-assured.rb +1 -45
  18. data/lib/rest-assured/api.rb +3 -0
  19. data/lib/rest-assured/api/app_runner.rb +18 -0
  20. data/lib/rest-assured/api/app_session.rb +33 -0
  21. data/lib/rest-assured/{client → api}/resources.rb +0 -0
  22. data/lib/rest-assured/api/server.rb +59 -0
  23. data/lib/rest-assured/application.rb +46 -0
  24. data/lib/rest-assured/config.rb +16 -3
  25. data/lib/rest-assured/models/redirect.rb +6 -0
  26. data/lib/rest-assured/routes/double.rb +1 -1
  27. data/lib/rest-assured/routes/redirect.rb +1 -1
  28. data/lib/rest-assured/routes/response.rb +4 -4
  29. data/lib/rest-assured/utils/drb_sniffer.rb +11 -0
  30. data/lib/rest-assured/utils/port_explorer.rb +22 -0
  31. data/lib/rest-assured/utils/subprocess.rb +50 -0
  32. data/lib/rest-assured/version.rb +1 -1
  33. data/prof-result.html +79521 -0
  34. data/rest-assured.gemspec +2 -1
  35. data/spec/api/app_runner_spec.rb +27 -0
  36. data/spec/api/app_session_spec.rb +51 -0
  37. data/spec/{client → api}/resource_double_spec.rb +1 -14
  38. data/spec/api/server_spec.rb +138 -0
  39. data/spec/config_spec.rb +111 -93
  40. data/spec/functional/response_spec.rb +5 -2
  41. data/spec/models/redirect_spec.rb +22 -0
  42. data/spec/port_explorer_spec.rb +32 -0
  43. data/spec/spec_helper.rb +25 -18
  44. data/spec/subprocess_spec.rb +138 -0
  45. data/spec/{custom_matchers.rb → support/custom_matchers.rb} +0 -0
  46. data/spec/support/reset-singleton.rb +15 -0
  47. metadata +66 -30
  48. data/lib/rest-assured/client.rb +0 -17
@@ -3,14 +3,18 @@ require 'rubygems'
3
3
  require 'spork'
4
4
 
5
5
  Spork.prefork do
6
+ require 'timeout'
6
7
  require 'rspec'
7
8
  require 'rack/test'
8
9
  require 'capybara'
9
10
  require 'capybara/firebug'
10
11
  require 'capybara/cucumber'
11
12
  require 'database_cleaner'
13
+ require 'awesome_print'
14
+ require 'rest-assured/utils/port_explorer'
12
15
  require File.dirname(__FILE__) + '/world_helpers'
13
16
 
17
+
14
18
  ENV['RACK_ENV'] = 'test'
15
19
 
16
20
  module RackHeaderHack
@@ -41,21 +45,8 @@ end
41
45
  Spork.each_run do
42
46
  require 'rest-assured/config'
43
47
  RestAssured::Config.build(:adapter => 'mysql')
44
-
45
48
  require 'rest-assured'
46
- require 'rest-assured/client'
47
- require File.expand_path('../test-server', __FILE__)
48
-
49
- at_exit do
50
- TestServer.stop
51
- end
52
-
53
- TestServer.start(:port => 9876, :db_user => ENV['TRAVIS'] ? "''" : "root")
54
-
55
- while not TestServer.up?
56
- puts 'Waiting for TestServer to come up...'
57
- sleep 1
58
- end
49
+ require 'rest-assured/application'
59
50
 
60
51
  def app
61
52
  RestAssured::Application
@@ -68,6 +59,10 @@ Spork.each_run do
68
59
  DatabaseCleaner.start
69
60
  end
70
61
 
62
+ Before "@ruby_api" do
63
+ RestAssured::Server.start(:port => 9876, :dbuser => ENV['TRAVIS'] ? "''" : "root")
64
+ end
65
+
71
66
  Before "@ui" do
72
67
  set_headers "HTTP_USER_AGENT" => 'Firefox'
73
68
  end
@@ -7,7 +7,7 @@ module WorldHelpers
7
7
  code = File.read rest_assured_exec
8
8
 
9
9
  code.sub!(/(.*)/, "\\1\nENV['RACK_ENV'] = 'production'")
10
- code.sub!(/require 'rest-assured'/, '')
10
+ code.sub!(/require 'rest-assured\/application'/, '')
11
11
  code.sub!(/RestAssured::Application.run!/, 'puts AppConfig.to_yaml')
12
12
 
13
13
  new_exec = "#{rest_assured_exec}_temp"
data/lib/rest-assured.rb CHANGED
@@ -1,45 +1 @@
1
- require 'sinatra/base'
2
- require 'sinatra/handler_options_patch'
3
- require 'haml'
4
- require 'rack-flash'
5
- require 'sinatra/partials'
6
- require 'rest-assured/config'
7
- require 'rest-assured/models/double'
8
- require 'rest-assured/models/redirect'
9
- require 'rest-assured/models/request'
10
- require 'rest-assured/routes/double'
11
- require 'rest-assured/routes/redirect'
12
- require 'rest-assured/routes/response'
13
-
14
- module RestAssured
15
- class Application < Sinatra::Base
16
-
17
- include Config
18
-
19
- enable :method_override
20
-
21
- enable :sessions
22
- use Rack::Flash, :sweep => true
23
-
24
- set :public_folder, File.expand_path('../../public', __FILE__)
25
- set :views, File.expand_path('../../views', __FILE__)
26
- set :haml, :format => :html5
27
-
28
- helpers Sinatra::Partials
29
-
30
- helpers do
31
- def browser?
32
- request.user_agent =~ /Safari|Firefox|Opera|MSIE|Chrome/
33
- end
34
- end
35
-
36
- include DoubleRoutes
37
- include RedirectRoutes
38
-
39
- %w{get post put delete}.each do |verb|
40
- send verb, /.*/ do
41
- Response.perform(self)
42
- end
43
- end
44
- end
45
- end
1
+ require 'rest-assured/api'
@@ -0,0 +1,3 @@
1
+ require 'rest-assured/api/server'
2
+ require 'rest-assured/api/resources'
3
+
@@ -0,0 +1,18 @@
1
+ require 'singleton'
2
+
3
+ module RestAssured
4
+ class AppRunner
5
+ include Singleton
6
+
7
+ def run!
8
+ unless Kernel.require 'rest-assured/application'
9
+ RestAssured::Application.send(:include, Config)
10
+ end
11
+ RestAssured::Application.run!
12
+ end
13
+
14
+ def self.method_missing(*args)
15
+ instance.send(*args)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,33 @@
1
+ require 'rest-assured/utils/subprocess'
2
+ require 'rest-assured/utils/drb_sniffer'
3
+ require 'rest-assured/api/app_runner'
4
+ require 'childprocess'
5
+
6
+ module RestAssured
7
+ class AppSession
8
+ include Utils::DrbSniffer
9
+
10
+ def initialize
11
+ @child = if not running_in_drb? and Process.respond_to?(:fork)
12
+ Utils::Subprocess.new do
13
+ AppRunner.run!
14
+ end
15
+ else
16
+ child = ChildProcess.build('rest-assured', *Config.to_cmdargs)
17
+ child.io.inherit!
18
+ child.start
19
+ child
20
+ end
21
+ end
22
+
23
+ def alive?
24
+ @child.alive?
25
+ rescue Errno::ECHILD
26
+ false
27
+ end
28
+
29
+ def method_missing(*args)
30
+ @child.send(*args)
31
+ end
32
+ end
33
+ end
File without changes
@@ -0,0 +1,59 @@
1
+ require 'singleton'
2
+ require 'rest-assured/config'
3
+ require 'rest-assured/api/resources'
4
+ require 'rest-assured/api/app_session'
5
+ require 'rest-assured/utils/port_explorer'
6
+
7
+ module RestAssured
8
+ class Server
9
+ attr_reader :address
10
+
11
+ include Singleton
12
+
13
+ def start!(opts = {})
14
+ at_exit { stop }
15
+
16
+ stop if up?
17
+
18
+ Config.build(opts)
19
+
20
+ self.address = "http#{AppConfig.ssl ? 's' : ''}://127.0.0.1:#{AppConfig.port}"
21
+
22
+ @session = AppSession.new
23
+ end
24
+
25
+ def start(*args)
26
+ start!(*args)
27
+
28
+ while not up?
29
+ sleep 0.5
30
+ end
31
+ end
32
+
33
+ def address=(address)
34
+ Double.site = @address = address
35
+ end
36
+
37
+ def stop
38
+ @session.try(:stop)
39
+
40
+ 10.times do
41
+ if up?
42
+ sleep 0.5
43
+ next
44
+ else
45
+ return
46
+ end
47
+ end
48
+ raise "Failed to stop RestAssured server"
49
+ end
50
+
51
+ def up?
52
+ !!@session && @session.alive? && !Utils::PortExplorer.port_free?(AppConfig.port)
53
+ end
54
+
55
+ def self.method_missing(*args)
56
+ instance.send(*args)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,46 @@
1
+ require 'sinatra/base'
2
+ require 'sinatra/handler_options_patch'
3
+ require 'haml'
4
+ require 'sinatra/flash'
5
+ require 'sinatra/partials'
6
+ require 'rest-assured/config'
7
+ require 'rest-assured/models/double'
8
+ require 'rest-assured/models/redirect'
9
+ require 'rest-assured/models/request'
10
+ require 'rest-assured/routes/double'
11
+ require 'rest-assured/routes/redirect'
12
+ require 'rest-assured/routes/response'
13
+
14
+ module RestAssured
15
+ class Application < Sinatra::Base
16
+
17
+ include Config
18
+
19
+ enable :method_override
20
+
21
+ enable :sessions
22
+ register Sinatra::Flash
23
+
24
+ set :public_folder, File.expand_path('../../../public', __FILE__)
25
+ set :views, File.expand_path('../../../views', __FILE__)
26
+ set :haml, :format => :html5
27
+
28
+ helpers Sinatra::Partials
29
+
30
+ helpers do
31
+ def browser?
32
+ request.user_agent =~ /Safari|Firefox|Opera|MSIE|Chrome/
33
+ end
34
+ end
35
+
36
+ include DoubleRoutes
37
+ include RedirectRoutes
38
+
39
+ %w{get post put delete}.each do |verb|
40
+ send verb, /.*/ do
41
+ Response.perform(self)
42
+ end
43
+ end
44
+ end
45
+ end
46
+
@@ -31,7 +31,8 @@ module RestAssured
31
31
 
32
32
  # this is meant to be called prior to include
33
33
  def self.build(opts = {})
34
- AppConfig.merge!(opts)
34
+ @user_conf = opts
35
+ AppConfig.merge!(@user_conf)
35
36
 
36
37
  AppConfig.logfile ||= if AppConfig.environment == 'production'
37
38
  './rest-assured.log'
@@ -45,7 +46,7 @@ module RestAssured
45
46
  def self.included(klass)
46
47
  init_logger
47
48
  setup_db
48
- setup_ssl(klass) if AppConfig.use_ssl
49
+ setup_ssl(klass) if AppConfig.ssl
49
50
 
50
51
  klass.set :port, AppConfig.port
51
52
  klass.set :environment, AppConfig.environment
@@ -54,6 +55,18 @@ module RestAssured
54
55
  klass.use Rack::CommonLogger, AppConfig.logger
55
56
  end
56
57
 
58
+ def self.to_cmdargs
59
+ @user_conf.inject([]) do |acc, (k,v)|
60
+ if v == true
61
+ acc << "--#{k}"
62
+ elsif v.is_a?(String) || v.is_a?(Integer)
63
+ acc << "--#{k}" << v.to_s
64
+ else
65
+ acc
66
+ end
67
+ end
68
+ end
69
+
57
70
  private
58
71
 
59
72
  def self.setup_ssl(klass)
@@ -152,7 +165,7 @@ module RestAssured
152
165
  end
153
166
 
154
167
  def self.build_ssl_config
155
- AppConfig.use_ssl ||= false
168
+ AppConfig.ssl ||= false
156
169
  AppConfig.ssl_cert ||= File.expand_path('../../../ssl/localhost.crt', __FILE__)
157
170
  AppConfig.ssl_key ||= File.expand_path('../../../ssl/localhost.key', __FILE__)
158
171
  end
@@ -9,6 +9,12 @@ module RestAssured
9
9
 
10
10
  before_create :assign_position
11
11
 
12
+ def self.find_redirect_url_for(fullpath)
13
+ if redirect = ordered.find { |r| fullpath =~ /#{r.pattern}/ }
14
+ fullpath.sub /#{redirect.pattern}/, redirect.to
15
+ end
16
+ end
17
+
12
18
  def self.update_order(ordered_redirect_ids)
13
19
  success = true
14
20
 
@@ -48,7 +48,7 @@ module RestAssured
48
48
  flash[:notice] = "Double created"
49
49
  redirect '/doubles'
50
50
  else
51
- flash[:error] = "Crumps! " + @double.errors.full_messages.join("; ")
51
+ flash.now[:error] = "Crumps! " + @double.errors.full_messages.join("; ")
52
52
  haml :'doubles/new'
53
53
  end
54
54
  else
@@ -19,7 +19,7 @@ module RestAssured
19
19
  flash[:notice] = "Redirect created"
20
20
  redirect '/redirects'
21
21
  else
22
- flash[:error] = "Crumps! " + @redirect.errors.full_messages.join("; ")
22
+ flash.now[:error] = "Crumps! " + @redirect.errors.full_messages.join("; ")
23
23
  haml :'redirects/new'
24
24
  end
25
25
  else
@@ -5,16 +5,16 @@ module RestAssured
5
5
 
6
6
  if d = Models::Double.where(:fullpath => request.fullpath, :active => true, :verb => request.request_method).first
7
7
  request.body.rewind
8
- body = request.body.read #without temp variable ':body = > body' is always nil. mistery
9
- env = request.env.except('rack.input', 'rack.errors', 'rack.logger')
8
+ body = request.body.read #without temp variable ':body = > body' is always nil. mistery
9
+ env = request.env.except('rack.input', 'rack.errors', 'rack.logger')
10
10
 
11
11
  d.requests.create!(:rack_env => env.to_json, :body => body, :params => request.params.to_json)
12
12
 
13
13
  app.headers d.response_headers
14
14
  app.body d.content
15
15
  app.status d.status
16
- elsif r = Models::Redirect.ordered.find { |r| request.fullpath =~ /#{r.pattern}/ }
17
- app.redirect( "#{r.to}#{request.fullpath}" )
16
+ elsif redirect_url = Models::Redirect.find_redirect_url_for(request.fullpath)
17
+ app.redirect redirect_url
18
18
  else
19
19
  app.status 404
20
20
  end
@@ -0,0 +1,11 @@
1
+ require 'drb'
2
+
3
+ module RestAssured
4
+ module Utils
5
+ module DrbSniffer
6
+ def running_in_drb?
7
+ defined?(Spork) and Spork.using_spork?
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ require 'socket'
2
+
3
+ module RestAssured
4
+ module Utils
5
+ class PortExplorer
6
+ def self.free_port
7
+ server = TCPServer.new('127.0.0.1', 0)
8
+ free_port = server.addr[1]
9
+ server.close
10
+ free_port
11
+ end
12
+
13
+ def self.port_free?(port)
14
+ Net::HTTP.get('127.0.0.1', '/', port)
15
+ rescue => e
16
+ e.is_a?(Errno::ECONNREFUSED)
17
+ else
18
+ false
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,50 @@
1
+ module RestAssured
2
+ module Utils
3
+ class Subprocess
4
+ attr_reader :pid
5
+
6
+ def initialize
7
+ @pid = Kernel.fork do
8
+ trap('USR1') do
9
+ $stopped = true
10
+ Process.kill('TERM', Process.pid) # unlike 'exit' this one is NOT being intercepted by Webrick
11
+ end
12
+
13
+ at_exit do
14
+ if $stopped
15
+ puts "Being stopped from parent..."
16
+ #else
17
+ #puts "Shutting down parent..."
18
+ #Process.kill('TERM', Process.ppid)
19
+ end
20
+ exit!
21
+ end
22
+
23
+ begin
24
+ yield
25
+ rescue => e
26
+ if defined?(EventMachine) && e.is_a?(EventMachine::ConnectionNotBound)
27
+ retry
28
+ end
29
+ puts "#{self} has raised #{e.inspect}:"
30
+ puts e.backtrace.join("\n")
31
+ end
32
+ end
33
+
34
+ Process.detach(@pid)
35
+ at_exit { stop }
36
+ end
37
+
38
+ def alive?
39
+ Process.kill(0, @pid)
40
+ true
41
+ rescue Errno::ESRCH
42
+ false
43
+ end
44
+
45
+ def stop
46
+ Process.kill('USR1', @pid) rescue Errno::ESRCH # no such process
47
+ end
48
+ end
49
+ end
50
+ end