rest-assured 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/Gemfile +17 -0
- data/LICENSE +22 -0
- data/README.markdown +78 -0
- data/Rakefile +4 -0
- data/bin/console +12 -0
- data/bin/rest-assured +28 -0
- data/cucumber.yml +3 -0
- data/db/migrate/20110620161740_add_fixtures.rb +12 -0
- data/db/migrate/20110625155332_add_redirects_table.rb +12 -0
- data/db/migrate/20110709150645_add_description_to_fixtures.rb +9 -0
- data/db/migrate/20110807222522_add_active_bit_to_fixtures.rb +9 -0
- data/db/migrate/20110818155555_add_position_to_redirects.rb +9 -0
- data/db/migrate/20110823132023_add_method_to_fixtures.rb +9 -0
- data/db/migrate/20110912162948_rename_url_to_fullpath.rb +9 -0
- data/db/migrate/20110912163705_rename_fixtures_to_doubles.rb +9 -0
- data/features/doubles_via_api.feature +50 -0
- data/features/doubles_via_ui.feature +66 -0
- data/features/persistence.feature +24 -0
- data/features/redirect_rules_via_api.feature +29 -0
- data/features/redirect_rules_via_ui.feature +68 -0
- data/features/step_definitions/doubles_steps.rb +143 -0
- data/features/step_definitions/persistence_steps.rb +13 -0
- data/features/step_definitions/redirect_rules_steps.rb +64 -0
- data/features/step_definitions/support/numeric_transforms.rb +3 -0
- data/features/support/env.rb +71 -0
- data/lib/rest-assured.rb +66 -0
- data/lib/rest-assured/config.rb +11 -0
- data/lib/rest-assured/init.rb +12 -0
- data/lib/rest-assured/models/double.rb +30 -0
- data/lib/rest-assured/models/redirect.rb +37 -0
- data/lib/rest-assured/routes/double.rb +77 -0
- data/lib/rest-assured/routes/redirect.rb +71 -0
- data/lib/rest-assured/version.rb +3 -0
- data/lib/sinatra/partials.rb +18 -0
- data/public/css/grid.inuit.css +76 -0
- data/public/css/inuit.css +904 -0
- data/public/css/jquery.jgrowl.css +132 -0
- data/public/css/style.css +88 -0
- data/public/img/css/12-grid-720.png +0 -0
- data/public/img/css/12-grid.png +0 -0
- data/public/img/css/baseline.gif +0 -0
- data/public/img/css/grid-720.png +0 -0
- data/public/img/css/grid.png +0 -0
- data/public/img/css/icons/error.png +0 -0
- data/public/img/css/icons/info.png +0 -0
- data/public/img/css/icons/success.png +0 -0
- data/public/img/css/icons/warning.png +0 -0
- data/public/javascript/application.js +12 -0
- data/public/javascript/jquery.jgrowl_minimized.js +11 -0
- data/rest-assured.gemspec +36 -0
- data/spec/functional/double_routes_spec.rb +117 -0
- data/spec/functional/redirect_routes_spec.rb +108 -0
- data/spec/models/double_spec.rb +73 -0
- data/spec/models/redirect_spec.rb +38 -0
- data/spec/spec_helper.rb +47 -0
- data/views/base.scss +11 -0
- data/views/doubles/_form.haml +16 -0
- data/views/doubles/edit.haml +4 -0
- data/views/doubles/index.haml +41 -0
- data/views/doubles/new.haml +3 -0
- data/views/layout.haml +25 -0
- data/views/redirects/_form.haml +11 -0
- data/views/redirects/edit.haml +4 -0
- data/views/redirects/index.haml +33 -0
- data/views/redirects/new.haml +3 -0
- 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,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
|
+
|
data/lib/rest-assured.rb
ADDED
@@ -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
|