Skip to content

Commit

Permalink
Changes redirects to support references to regex captured groups
Browse files Browse the repository at this point in the history
  • Loading branch information
artemave committed Feb 8, 2012
1 parent eaedcfe commit 02d8bf0
Show file tree
Hide file tree
Showing 11 changed files with 58 additions and 23 deletions.
1 change: 1 addition & 0 deletions .rspec
@@ -0,0 +1 @@
--color
1 change: 1 addition & 0 deletions Gemfile
Expand Up @@ -3,6 +3,7 @@ source "http://rubygems.org"
# Specify your gem's dependencies in rest-assured.gemspec
gemspec

gem 'awesome_print'
gem 'cucumber'
gem 'database_cleaner'
gem 'rspec'
Expand Down
4 changes: 2 additions & 2 deletions README.markdown
Expand Up @@ -163,7 +163,7 @@ For those using rest-assured from non-ruby environments.

It is sometimes desirable to only double certain calls while letting others through to the 'real' services. Meet Redirects. Kind of "rewrite rules" for requests that didn't match any double.

Another potential use for redirects is setting up a 'default' double that matches multiple fullpaths. This is of course given your app does not mind an extra redirect. Also note that 'default' double still covers only one http verb so requests with different methods won't match.
Another potential use for redirects is setting up a 'default' double that matches multiple fullpaths. This is of course given your app does not mind an extra redirect. Also note that 'default' double still covers single http verb so requests with different methods won't match.

Here is the rest API for managing redirects:

Expand All @@ -179,7 +179,7 @@ Here is the rest API for managing redirects:

bash$ curl -d 'pattern=^/auth&to=https://myserver.com/api' http://localhost:4578/redirects

Now request (any verb) to 'http://localhost:4578/auth/services/1' will get redirected to 'https://myserver.com/api/auth/services/1.' Provided of course there is no double matched for that fullpath and verb.
Now request (any verb) to 'http://localhost:4578/auth/services/1' will get redirected to 'https://myserver.com/api/'. Provided of course there is no double matched for that fullpath and verb. Captured group in pattern can be referenced from replacement e.g. \\1, \\2, etc.
Much like rewrite rules, redirects are evaluated in order (of creation). In UI you can manually rearrange the order.

### Delete all redirects
Expand Down
19 changes: 10 additions & 9 deletions features/rest_api/redirects.feature
@@ -1,3 +1,4 @@
@now
Feature: manage redirect rules
In order to be able to mock only part of api
As a developer
Expand All @@ -10,21 +11,21 @@ Feature: manage redirect rules

Scenario: add redirect rule
Given blank slate
When I register redirect with pattern "^/api" and uri "http://real.api.co.uk"
When I create redirect from "^/api/(.*)" to "http://example.com/\1"
And I request "/api/something"
Then it should redirect to "http://real.api.co.uk/api/something"
Then it should redirect to "http://example.com/something"

Scenario: add second redirect that match the same request
Given there is redirect with pattern "/api/something" and uri "http://real.api.co.uk"
When I register redirect with pattern "/api/some.*" and uri "http://real.com"
Given there is redirect from "/api/something" to "http://example.com"
When I create redirect from "/api/some.*" to "http://real.com"
And I request "/api/something"
Then it should redirect to "http://real.api.co.uk/api/something"
Then it should redirect to "http://example.com/"

Scenario: add second redirect that does not match the same request
Given there is redirect with pattern "/api/something" and uri "http://real.api.co.uk"
When I register redirect with pattern "/api/some" and uri "http://real.com"
And I request "/api/someth"
Then it should redirect to "http://real.com/api/someth"
Given there is redirect from "/api/thing" to "http://real.com"
When I create redirect from "/api/some.*" to "http://example.com"
And I request "/api/something"
Then it should redirect to "http://example.com/"

Scenario: clear redirects
Given there are some redirects
Expand Down
4 changes: 4 additions & 0 deletions features/step_definitions/doubles_steps.rb
Expand Up @@ -39,6 +39,10 @@
get fullpath
end

When /sleep (\d+)/ do |n|
sleep n
end

When /^I "([^"]*)" "([^"]*)"$/ do |verb, fullpath|
send(verb.downcase, fullpath)
end
Expand Down
8 changes: 2 additions & 6 deletions features/step_definitions/redirect_rules_steps.rb
Expand Up @@ -2,18 +2,14 @@
last_response.status.should.to_s == code
end

Given /^there is redirect with pattern "([^"]*)" and uri "([^"]*)"$/ do |pattern, url|
When /^(?:I create|there is) redirect from "([^"]*)" to "([^"]*)"$/ do |pattern, url|
post '/redirects', { :pattern => pattern, :to => url }
last_response.should be_ok
end

When /^I register redirect with pattern "([^"]*)" and uri "([^"]*)"$/ do |pattern, url|
step %{there is redirect with pattern "#{pattern}" and uri "#{url}"}
end

Then /^it should redirect to "([^"]*)"$/ do |real_api_url|
follow_redirect!
last_response.header['Location'].should == real_api_url
last_request.url.should == real_api_url
end

Given /^the following redirects exist:$/ do |redirects|
Expand Down
1 change: 1 addition & 0 deletions features/support/env.rb
Expand Up @@ -9,6 +9,7 @@
require 'capybara/firebug'
require 'capybara/cucumber'
require 'database_cleaner'
require 'awesome_print'
require File.dirname(__FILE__) + '/world_helpers'

ENV['RACK_ENV'] = 'test'
Expand Down
6 changes: 6 additions & 0 deletions lib/rest-assured/models/redirect.rb
Expand Up @@ -9,6 +9,12 @@ class Redirect < ActiveRecord::Base

before_create :assign_position

def self.find_redirect_url_for(fullpath)
if redirect = ordered.find { |r| fullpath =~ /#{r.pattern}/ }
fullpath.sub /#{redirect.pattern}/, redirect.to
end
end

def self.update_order(ordered_redirect_ids)
success = true

Expand Down
8 changes: 4 additions & 4 deletions lib/rest-assured/routes/response.rb
Expand Up @@ -5,16 +5,16 @@ def self.perform(app)

if d = Models::Double.where(:fullpath => request.fullpath, :active => true, :verb => request.request_method).first
request.body.rewind
body = request.body.read #without temp variable ':body = > body' is always nil. mistery
env = request.env.except('rack.input', 'rack.errors', 'rack.logger')
body = request.body.read #without temp variable ':body = > body' is always nil. mistery
env = request.env.except('rack.input', 'rack.errors', 'rack.logger')

d.requests.create!(:rack_env => env.to_json, :body => body, :params => request.params.to_json)

app.headers d.response_headers
app.body d.content
app.status d.status
elsif r = Models::Redirect.ordered.find { |r| request.fullpath =~ /#{r.pattern}/ }
app.redirect( "#{r.to}#{request.fullpath}" )
elsif redirect_url = Models::Redirect.find_redirect_url_for(request.fullpath)
app.redirect redirect_url
else
app.status 404
end
Expand Down
7 changes: 5 additions & 2 deletions spec/functional/response_spec.rb
Expand Up @@ -63,11 +63,13 @@ module RestAssured
end

it "redirects if double not hit but there is redirect that matches request" do
r = Models::Redirect.create :to => 'http://exmple.com/api', :pattern => '.*'
#r = Models::Redirect.create :to => 'http://exmple.com/api', :pattern => '.*'
#
fullpath = '/some/other/path'
request.stub(:fullpath).and_return(fullpath)
Models::Redirect.stub(:find_redirect_url_for).with(fullpath).and_return('new_url')

rest_assured_app.should_receive(:redirect).with(r.to + fullpath)
rest_assured_app.should_receive(:redirect).with('new_url')

Response.perform(rest_assured_app)
end
Expand All @@ -78,6 +80,7 @@ module RestAssured
Response.perform(rest_assured_app)
end

# TODO change to instead exclude anything that does not respond_to?(:to_s)
it 'excludes "rack.input" and "rack.errors" as they break with "IOError - not opened for reading:" on consequent #to_json (as they are IO and StringIO)' do
requests = double.as_null_object
Models::Double.stub_chain('where.first').and_return(double(:requests => requests).as_null_object)
Expand Down
22 changes: 22 additions & 0 deletions spec/models/redirect_spec.rb
Expand Up @@ -37,5 +37,27 @@ module RestAssured::Models

Redirect.update_order([nil, 34]).should == false
end

context 'redirect url' do
it 'constructs url to redirect to' do
path = rand(1000)
r = Redirect.create :pattern => '/api/(.*)\?.*', :to => 'http://external.com/some/url/\1?p=5'
Redirect.find_redirect_url_for("/api/#{path}?param=1").should == "http://external.com/some/url/#{path}?p=5"
end

it 'returns the one that matches the substring' do
r1 = Redirect.create :pattern => '/ai/path', :to => 'someurl'
r2 = Redirect.create :pattern => '/api/path', :to => 'someurl'

Redirect.find_redirect_url_for('/api/path').should == 'someurl'
end

it 'returns the oldest one that match' do
r1 = Redirect.create :pattern => '/api', :to => 'someurl'
r2 = Redirect.create :pattern => '/api/path', :to => 'otherurl'

Redirect.find_redirect_url_for('/api/path').should == 'someurl/path'
end
end
end
end

0 comments on commit 02d8bf0

Please sign in to comment.