rest-assured 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/.gitignore +7 -0
  2. data/Gemfile +17 -0
  3. data/LICENSE +22 -0
  4. data/README.markdown +78 -0
  5. data/Rakefile +4 -0
  6. data/bin/console +12 -0
  7. data/bin/rest-assured +28 -0
  8. data/cucumber.yml +3 -0
  9. data/db/migrate/20110620161740_add_fixtures.rb +12 -0
  10. data/db/migrate/20110625155332_add_redirects_table.rb +12 -0
  11. data/db/migrate/20110709150645_add_description_to_fixtures.rb +9 -0
  12. data/db/migrate/20110807222522_add_active_bit_to_fixtures.rb +9 -0
  13. data/db/migrate/20110818155555_add_position_to_redirects.rb +9 -0
  14. data/db/migrate/20110823132023_add_method_to_fixtures.rb +9 -0
  15. data/db/migrate/20110912162948_rename_url_to_fullpath.rb +9 -0
  16. data/db/migrate/20110912163705_rename_fixtures_to_doubles.rb +9 -0
  17. data/features/doubles_via_api.feature +50 -0
  18. data/features/doubles_via_ui.feature +66 -0
  19. data/features/persistence.feature +24 -0
  20. data/features/redirect_rules_via_api.feature +29 -0
  21. data/features/redirect_rules_via_ui.feature +68 -0
  22. data/features/step_definitions/doubles_steps.rb +143 -0
  23. data/features/step_definitions/persistence_steps.rb +13 -0
  24. data/features/step_definitions/redirect_rules_steps.rb +64 -0
  25. data/features/step_definitions/support/numeric_transforms.rb +3 -0
  26. data/features/support/env.rb +71 -0
  27. data/lib/rest-assured.rb +66 -0
  28. data/lib/rest-assured/config.rb +11 -0
  29. data/lib/rest-assured/init.rb +12 -0
  30. data/lib/rest-assured/models/double.rb +30 -0
  31. data/lib/rest-assured/models/redirect.rb +37 -0
  32. data/lib/rest-assured/routes/double.rb +77 -0
  33. data/lib/rest-assured/routes/redirect.rb +71 -0
  34. data/lib/rest-assured/version.rb +3 -0
  35. data/lib/sinatra/partials.rb +18 -0
  36. data/public/css/grid.inuit.css +76 -0
  37. data/public/css/inuit.css +904 -0
  38. data/public/css/jquery.jgrowl.css +132 -0
  39. data/public/css/style.css +88 -0
  40. data/public/img/css/12-grid-720.png +0 -0
  41. data/public/img/css/12-grid.png +0 -0
  42. data/public/img/css/baseline.gif +0 -0
  43. data/public/img/css/grid-720.png +0 -0
  44. data/public/img/css/grid.png +0 -0
  45. data/public/img/css/icons/error.png +0 -0
  46. data/public/img/css/icons/info.png +0 -0
  47. data/public/img/css/icons/success.png +0 -0
  48. data/public/img/css/icons/warning.png +0 -0
  49. data/public/javascript/application.js +12 -0
  50. data/public/javascript/jquery.jgrowl_minimized.js +11 -0
  51. data/rest-assured.gemspec +36 -0
  52. data/spec/functional/double_routes_spec.rb +117 -0
  53. data/spec/functional/redirect_routes_spec.rb +108 -0
  54. data/spec/models/double_spec.rb +73 -0
  55. data/spec/models/redirect_spec.rb +38 -0
  56. data/spec/spec_helper.rb +47 -0
  57. data/views/base.scss +11 -0
  58. data/views/doubles/_form.haml +16 -0
  59. data/views/doubles/edit.haml +4 -0
  60. data/views/doubles/index.haml +41 -0
  61. data/views/doubles/new.haml +3 -0
  62. data/views/layout.haml +25 -0
  63. data/views/redirects/_form.haml +11 -0
  64. data/views/redirects/edit.haml +4 -0
  65. data/views/redirects/index.haml +33 -0
  66. data/views/redirects/new.haml +3 -0
  67. metadata +250 -0
@@ -0,0 +1,68 @@
1
+ @ui
2
+ Feature: manage redirects via ui
3
+ In order to set up 'pass through' for requests that don't match any double
4
+ As a developer
5
+ I want to have a ui to set up redirects
6
+
7
+ Scenario: view existing redirects
8
+ Given the following redirects exist:
9
+ | pattern | to |
10
+ | /url1/.* | http://google.com/api |
11
+ | /url2/bbb | http://twitter.com/api |
12
+ When I visit "redirects" page
13
+ Then I should see that I am on "redirects" page
14
+ And I should see existing redirects:
15
+ | pattern | to |
16
+ | /url1/.* | http://google.com/api |
17
+ | /url2/bbb | http://twitter.com/api |
18
+
19
+ Scenario: add new redirect
20
+ Given I visit "redirects" page
21
+ When I choose to create a redirect
22
+ And I enter redirect details:
23
+ | pattern | to |
24
+ | /url1/.* | http://google.com/api |
25
+ And I save it
26
+ Then I should see "Redirect created"
27
+ And I should see existing redirects:
28
+ | pattern | to |
29
+ | /url1/.* | http://google.com/api |
30
+
31
+ Scenario: edit redirect
32
+ Given the following redirects exist:
33
+ | pattern | to |
34
+ | /url1/.* | http://google.com/api |
35
+ And I visit "redirects" page
36
+ And I choose to edit redirect
37
+ When I change "redirect" "pattern" to "/some/remote.*"
38
+ And I save it
39
+ Then I should see that I am on "redirects" page
40
+ And I should see existing redirects:
41
+ | pattern | to |
42
+ | /some/remote.* | http://google.com/api |
43
+
44
+ # XXX This does not pass due to Capybara/Selelium broken drag and drop support
45
+ @javascript @pending
46
+ Scenario: reorder redirects
47
+ Given the following redirects exist:
48
+ | pattern | to |
49
+ | /api/.* | http://google.com/api |
50
+ | /api/bbb | http://twitter.com/api |
51
+ And I am on "redirects" page
52
+ When I reorder second redirect to be the first one
53
+ Then "/api/bbb" should be redirected to "http://twitter.com/api"
54
+ When I reorder second redirect to be the first one
55
+ Then "/api/bbb" should be redirected to "http://google.com/api"
56
+
57
+ @javascript
58
+ Scenario: delete redirect
59
+ Given the following redirects exist:
60
+ | pattern | to |
61
+ | /url1/.* | http://google.com/api |
62
+ | /url2/bbb | http://twitter.com/api |
63
+ And I visit "redirects" page
64
+ And I choose to delete redirect with pattern "/url1/.*"
65
+ Then I should be asked to confirm delete
66
+ And I should see "Redirect deleted"
67
+ And I should not see "/url1/.*"
68
+ And I should see "/url2/bbb"
@@ -0,0 +1,143 @@
1
+ # REST api steps
2
+
3
+ Given /^there are no doubles$/ do
4
+ Double.destroy_all
5
+ end
6
+
7
+ When /^I create a double with "([^"]*)" as fullpath and "([^"]*)" as response content$/ do |fullpath, content|
8
+ post '/doubles', { :fullpath => fullpath, :content => content }
9
+ last_response.should be_ok
10
+ end
11
+
12
+ When /^I create a double with "([^"]*)" as fullpath, "([^"]*)" as response content and "([^"]*)" as request method$/ do |fullpath, content, method|
13
+ post '/doubles', { :fullpath => fullpath, :content => content, :method => method }
14
+ last_response.should be_ok
15
+ end
16
+
17
+ Then /^there should be (#{CAPTURE_A_NUMBER}) double with "([^"]*)" as fullpath and "([^"]*)" as response content$/ do |n, fullpath, content|
18
+ Double.where(:fullpath => fullpath, :content => content).count.should == 1
19
+ end
20
+
21
+ Then /^there should be (#{CAPTURE_A_NUMBER}) double with "([^"]*)" as fullpath, "([^"]*)" as response content and "([^"]*)" as request method$/ do |n, fullpath, content, method|
22
+ Double.where(:fullpath => fullpath, :content => content, :method => method).count.should == n
23
+ end
24
+
25
+ Given /^there is double with "([^"]*)" as fullpath and "([^"]*)" as response content$/ do |fullpath, content|
26
+ Double.create(:fullpath => fullpath, :content => content)
27
+ end
28
+
29
+ Given /^there is double with "([^"]*)" as fullpath, "([^"]*)" as response content and "([^"]*)" as request method$/ do |fullpath, content, method|
30
+ Double.create(:fullpath => fullpath, :content => content, :method => method)
31
+ end
32
+
33
+ Given /^I register "([^"]*)" as fullpath and "([^"]*)" as response content$/ do |fullpath, content|
34
+ post '/doubles', { :fullpath => fullpath, :content => content }
35
+ last_response.should be_ok
36
+ end
37
+
38
+ When /^I request "([^"]*)"$/ do |fullpath|
39
+ get fullpath
40
+ end
41
+
42
+ When /^I "([^"]*)" "([^"]*)"$/ do |method, fullpath|
43
+ send(method.downcase, fullpath)
44
+ end
45
+
46
+ Then /^I should get "([^"]*)" in response content$/ do |content|
47
+ last_response.body.should == content
48
+ end
49
+
50
+ Given /^there are some doubles$/ do
51
+ [['fullpath1', 'content1'], ['fullpath2', 'content2'], ['fullpath3', 'content3']].each do |double|
52
+ Double.create(:fullpath => double[0], :content => double[1])
53
+ end
54
+ end
55
+
56
+ When /^I delete all doubles$/ do
57
+ delete '/doubles/all'
58
+ last_response.should be_ok
59
+ end
60
+
61
+ Then /^there should be no doubles$/ do
62
+ Double.count.should == 0
63
+ end
64
+
65
+ # UI steps
66
+
67
+ Given /^the following doubles exist:$/ do |doubles|
68
+ doubles.hashes.each do |row|
69
+ Double.create(:fullpath => row['fullpath'], :description => row['description'], :content => row['content'])
70
+ end
71
+ end
72
+
73
+ Then /^I should see that I am on "([^""]*)" page$/ do |name|
74
+ find('title').text.should =~ /#{name} -/
75
+ end
76
+
77
+ Then /^I should see existing doubles:$/ do |doubles|
78
+ doubles.hashes.each do |row|
79
+ page.should have_content(row[:fullpath])
80
+ page.should have_content(row[:description])
81
+ end
82
+ end
83
+
84
+ Given /^I am on "([^"]*)" page$/ do |page|
85
+ When "I visit \"#{page}\" page"
86
+ end
87
+
88
+ When /^I choose to create a double$/ do
89
+ find(:xpath, '//a[text()="New double"]').click
90
+ end
91
+
92
+ When /^I enter double details:$/ do |details|
93
+ double = details.hashes.first
94
+
95
+ fill_in 'Request fullpath', :with => double['fullpath']
96
+ fill_in 'Content', :with => double['content']
97
+ fill_in 'Description', :with => double['description']
98
+ end
99
+
100
+ When /^I save it$/ do
101
+ find('input[type="submit"]').click
102
+ end
103
+
104
+ Then /^I should (not)? ?see "([^"]*)"$/ do |see, text|
105
+ if see == 'not'
106
+ page.should_not have_content(text)
107
+ else
108
+ page.should have_content(text)
109
+ end
110
+ end
111
+
112
+ Given /^there are two doubles for the same fullpath$/ do
113
+ @first = Double.create :fullpath => '/api/something', :content => 'some content'
114
+ @second = Double.create :fullpath => '/api/something', :content => 'other content'
115
+ end
116
+
117
+ When /^I make (first|second) double active$/ do |ord|
118
+ within "#double_row_#{instance_variable_get('@' + ord).id}" do
119
+ find('input[type="radio"]').click
120
+ end
121
+ end
122
+
123
+ Then /^(first|second) double should be served$/ do |ord|
124
+ f = instance_variable_get('@' + ord)
125
+ get f.fullpath
126
+ last_response.body.should == f.content
127
+ end
128
+
129
+ Given /^I choose to edit (?:double|redirect)$/ do
130
+ find('.edit-link a').click
131
+ end
132
+
133
+ When /^I change "([^"]*)" "([^"]*)" to "([^"]*)"$/ do |obj, prop, value|
134
+ fill_in "#{obj}_#{prop}", :with => value
135
+ end
136
+
137
+ Given /^I choose to delete double with fullpath "([^"]*)"$/ do |fullpath|
138
+ find(:xpath, "//tr[td[text()='#{fullpath}']]//a[text()='Delete']").click
139
+ end
140
+
141
+ Then /^I should be asked to confirm delete$/ do
142
+ page.driver.browser.switch_to.alert.accept
143
+ end
@@ -0,0 +1,13 @@
1
+ Given /^I (?:re)?start service without \-\-database option$/ do
2
+ AppConfig[:database] = ':memory:' #default value
3
+ load 'rest-assured/init.rb'
4
+ end
5
+
6
+ Then /^I should get (#{CAPTURE_A_NUMBER}) in response status$/ do |status|
7
+ last_response.status.should == status
8
+ end
9
+
10
+ Given /^I (?:re)?start service with \-\-database "([^"]*)" option$/ do |db_path|
11
+ AppConfig[:database] = db_path
12
+ load 'rest-assured/init.rb'
13
+ end
@@ -0,0 +1,64 @@
1
+ Given /^there are no redirect rules$/ do
2
+ Redirect.destroy_all
3
+ end
4
+
5
+ Then /^I should get (\d+)$/ do |code|
6
+ last_response.status.should.to_s == code
7
+ end
8
+
9
+ When /^I register redirect with pattern "([^"]*)" and uri "([^"]*)"$/ do |pattern, uri|
10
+ post '/redirects', { :pattern => pattern, :to => uri }
11
+ last_response.should be_ok
12
+ end
13
+
14
+ Then /^it should redirect to "([^"]*)"$/ do |real_api_url|
15
+ follow_redirect!
16
+ last_response.header['Location'].should == real_api_url
17
+ end
18
+
19
+ Given /^the following redirects exist:$/ do |redirects|
20
+ redirects.hashes.each do |row|
21
+ Redirect.create(:pattern => row['pattern'], :to => row['to'])
22
+ end
23
+ end
24
+
25
+ When /^I visit "([^"]+)" page$/ do |page|
26
+ visit '/'
27
+ find(:xpath, "//a[text()='#{page.capitalize}']").click
28
+ end
29
+
30
+ When /^I choose to create a redirect$/ do
31
+ find(:xpath, '//a[text()="New redirect"]').click
32
+ end
33
+
34
+ When /^I enter redirect details:$/ do |details|
35
+ redirect = details.hashes.first
36
+
37
+ fill_in 'Pattern', :with => redirect['pattern']
38
+ fill_in 'Redirect to', :with => redirect['to']
39
+ end
40
+
41
+ Then /^I should see existing redirects:$/ do |redirects|
42
+ redirects.hashes.each do |row|
43
+ page.should have_content(row[:pattern])
44
+ page.should have_content(row[:to])
45
+ end
46
+ end
47
+
48
+ Given /^I choose to delete redirect with pattern "([^"]*)"$/ do |pattern|
49
+ find(:xpath, "//tr[td[text()='#{pattern}']]//a[text()='Delete']").click
50
+ end
51
+
52
+ When /^I reorder second redirect to be the first one$/ do
53
+ handler = find("#redirects #redirect_#{Redirect.last.id} td.handle")
54
+ target = find('#redirects thead')
55
+
56
+ handler.drag_to target
57
+ end
58
+
59
+ Then /^"([^"]*)" should be redirected to "([^"]*)"$/ do |missing_request, url|
60
+ get missing_request
61
+ follow_redirect!
62
+
63
+ last_request.url.should == "#{url}#{missing_request}"
64
+ end
@@ -0,0 +1,3 @@
1
+ CAPTURE_A_NUMBER = Transform /^\d+$/ do |n|
2
+ n.to_i
3
+ end
@@ -0,0 +1,71 @@
1
+ $:.push File.expand_path('../../../lib', __FILE__)
2
+ require 'rspec/expectations'
3
+ require 'rack/test'
4
+ require 'capybara'
5
+ require 'capybara/firebug'
6
+ require 'capybara/cucumber'
7
+ require 'database_cleaner'
8
+ require 'logger'
9
+
10
+ ENV['RACK_ENV'] = 'test'
11
+
12
+ require 'rest-assured'
13
+
14
+ module RackHeaderHack
15
+ def set_headers(headers)
16
+ browser = page.driver.browser
17
+ def browser.env
18
+ @env.merge(super)
19
+ end
20
+ def browser.env=(env)
21
+ @env = env
22
+ end
23
+ browser.env = headers
24
+ end
25
+ end
26
+
27
+ def setup_logger
28
+ Logger.class_eval do
29
+ alias_method :write, :<<
30
+ end
31
+
32
+ logger = Logger.new(File.expand_path("../../../test.log", __FILE__))
33
+ logger.level = Logger::DEBUG
34
+
35
+ RestAssured::Application.class_eval do
36
+ use Rack::CommonLogger, logger
37
+ end
38
+
39
+ ActiveRecord::Base.logger = logger
40
+ end
41
+
42
+ setup_logger
43
+
44
+ def app
45
+ RestAssured::Application
46
+ end
47
+ Capybara.app = app
48
+
49
+ Capybara.register_driver :selenium do |app|
50
+ profile = Selenium::WebDriver::Firefox::Profile.new
51
+ profile.enable_firebug
52
+
53
+ Capybara::Selenium::Driver.new(app, :browser => :firefox, :profile => profile)
54
+ end
55
+
56
+ World(Capybara, Rack::Test::Methods, RackHeaderHack)
57
+
58
+ DatabaseCleaner.strategy = :truncation
59
+
60
+ Before do
61
+ DatabaseCleaner.start
62
+ end
63
+
64
+ Before "@ui" do
65
+ set_headers "HTTP_USER_AGENT" => 'Firefox'
66
+ end
67
+
68
+ After do
69
+ DatabaseCleaner.clean
70
+ end
71
+
@@ -0,0 +1,66 @@
1
+ require 'rubygems'
2
+ require 'sinatra/base'
3
+ require 'haml'
4
+ require 'sass'
5
+ require 'sinatra/static_assets'
6
+ #require 'sinatra/reloader'
7
+ require 'rack-flash'
8
+ require 'sinatra/partials'
9
+ require 'rest-assured/init'
10
+ require 'rest-assured/models/double'
11
+ require 'rest-assured/models/redirect'
12
+ require 'rest-assured/routes/double'
13
+ require 'rest-assured/routes/redirect'
14
+
15
+ module RestAssured
16
+ class Application < Sinatra::Base
17
+ set :environment, AppConfig[:environment]
18
+ set :port, AppConfig[:port]
19
+
20
+ enable :method_override
21
+
22
+ enable :logging
23
+
24
+ enable :sessions
25
+ use Rack::Flash, :sweep => true
26
+
27
+ set :public, File.expand_path('../../public', __FILE__)
28
+ set :views, File.expand_path('../../views', __FILE__)
29
+ set :haml, :format => :html5
30
+
31
+ helpers Sinatra::Partials
32
+ register Sinatra::StaticAssets
33
+
34
+ helpers do
35
+ def browser?
36
+ request.user_agent =~ /Safari|Firefox|Opera|MSIE|Chrome/
37
+ end
38
+ end
39
+
40
+ include DoubleRoutes
41
+ include RedirectRoutes
42
+
43
+ get '/css/base.css' do
44
+ scss :base
45
+ end
46
+
47
+ %w{get post put delete}.each do |method|
48
+ send method, /.*/ do
49
+ Double.where(:fullpath => request.fullpath, :active => true, :method => method.upcase).first.try(:content) or try_redirect(request) or status 404
50
+ end
51
+ end
52
+
53
+ #configure(:development) do
54
+ #register Sinatra::Reloader
55
+ #end
56
+
57
+ private
58
+ def try_redirect(request)
59
+ r = Redirect.ordered.find do |r|
60
+ request.fullpath =~ /#{r.pattern}/
61
+ end
62
+
63
+ r && redirect( "#{r.to}#{request.fullpath}" )
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,11 @@
1
+ AppConfig = {
2
+ :port => 4578,
3
+ :environment => ENV['RACK_ENV'] || 'production'
4
+ }
5
+
6
+ AppConfig[:database] = case AppConfig[:environment]
7
+ when 'production'
8
+ './rest-assured.db'
9
+ else
10
+ File.expand_path("../../../db/#{AppConfig[:environment]}.db", __FILE__)
11
+ end