agile-proxy-jruby 0.1.25-jruby
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.
- checksums.yaml +7 -0
- data/.bowerrc +3 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.rubocop.yml +36 -0
- data/.travis.yml +10 -0
- data/Gemfile +4 -0
- data/Guardfile +20 -0
- data/LICENSE +22 -0
- data/README.md +131 -0
- data/Rakefile +15 -0
- data/agile-proxy.gemspec +60 -0
- data/assets/index.html +39 -0
- data/assets/ui/app/AgileProxyApi.js +31 -0
- data/assets/ui/app/app.js +1 -0
- data/assets/ui/app/controller/Stubs.js +64 -0
- data/assets/ui/app/controller/main.js +12 -0
- data/assets/ui/app/directive/AppEnhancedFormElement.js +21 -0
- data/assets/ui/app/directive/AppFor.js +16 -0
- data/assets/ui/app/directive/AppResponseEditor.js +54 -0
- data/assets/ui/app/model/RequestSpec.js +6 -0
- data/assets/ui/app/routes.js +11 -0
- data/assets/ui/app/service/Dialog.js +49 -0
- data/assets/ui/app/service/DomId.js +10 -0
- data/assets/ui/app/service/Error.js +7 -0
- data/assets/ui/app/service/Stub.js +36 -0
- data/assets/ui/app/view/404.html +2 -0
- data/assets/ui/app/view/dialog/error.html +10 -0
- data/assets/ui/app/view/dialog/yesNo.html +8 -0
- data/assets/ui/app/view/responses/editForm.html +78 -0
- data/assets/ui/app/view/status.html +1 -0
- data/assets/ui/app/view/stubs.html +19 -0
- data/assets/ui/app/view/stubs/edit.html +58 -0
- data/assets/ui/css/main.css +3 -0
- data/bin/agile_proxy +4 -0
- data/bower.json +27 -0
- data/config.yml +6 -0
- data/db.yml +10 -0
- data/db/migrations/20140818110800_create_users.rb +9 -0
- data/db/migrations/20140818134700_create_applications.rb +10 -0
- data/db/migrations/20140818135200_create_request_specs.rb +13 -0
- data/db/migrations/20140821115300_create_responses.rb +14 -0
- data/db/migrations/20140823082900_add_method_to_request_specs.rb +7 -0
- data/db/migrations/20140823083900_rename_request_spec_columns.rb +8 -0
- data/db/migrations/20141031072100_add_url_type_to_request_specs.rb +8 -0
- data/db/migrations/20141105125600_add_conditions_to_request_specs.rb +7 -0
- data/db/migrations/20141106083100_add_username_and_password_to_applications.rb +8 -0
- data/db/migrations/20141119143800_add_record_to_applications.rb +7 -0
- data/db/migrations/20141119174300_create_recordings.rb +18 -0
- data/db/migrations/20150221152500_add_record_requests_to_request_specs.rb +7 -0
- data/db/schema.rb +78 -0
- data/db/seed.rb +26 -0
- data/echo_server.rb +19 -0
- data/examples/README.md +1 -0
- data/examples/facebook_api.html +59 -0
- data/examples/tumblr_api.html +22 -0
- data/lib/agile_proxy.rb +8 -0
- data/lib/agile_proxy/api/applications.rb +77 -0
- data/lib/agile_proxy/api/recordings.rb +52 -0
- data/lib/agile_proxy/api/request_spec_recordings.rb +52 -0
- data/lib/agile_proxy/api/request_specs.rb +86 -0
- data/lib/agile_proxy/api/root.rb +45 -0
- data/lib/agile_proxy/cli.rb +116 -0
- data/lib/agile_proxy/config.rb +66 -0
- data/lib/agile_proxy/handlers/handler.rb +43 -0
- data/lib/agile_proxy/handlers/proxy_handler.rb +111 -0
- data/lib/agile_proxy/handlers/request_handler.rb +75 -0
- data/lib/agile_proxy/handlers/stub_handler.rb +146 -0
- data/lib/agile_proxy/mitm.crt +22 -0
- data/lib/agile_proxy/mitm.key +27 -0
- data/lib/agile_proxy/model/application.rb +20 -0
- data/lib/agile_proxy/model/recording.rb +17 -0
- data/lib/agile_proxy/model/request_spec.rb +48 -0
- data/lib/agile_proxy/model/response.rb +51 -0
- data/lib/agile_proxy/model/user.rb +17 -0
- data/lib/agile_proxy/proxy_connection.rb +112 -0
- data/lib/agile_proxy/rack/get_only_cache.rb +30 -0
- data/lib/agile_proxy/route.rb +106 -0
- data/lib/agile_proxy/router.rb +99 -0
- data/lib/agile_proxy/server.rb +119 -0
- data/lib/agile_proxy/servers/api.rb +40 -0
- data/lib/agile_proxy/servers/request_spec.rb +40 -0
- data/lib/agile_proxy/servers/request_spec_direct.rb +35 -0
- data/lib/agile_proxy/version.rb +6 -0
- data/load_proxy.js +39 -0
- data/log/.gitkeep +0 -0
- data/spec/common_helper.rb +32 -0
- data/spec/fixtures/example_static_file.html +1 -0
- data/spec/fixtures/test-server.crt +15 -0
- data/spec/fixtures/test-server.key +15 -0
- data/spec/integration/helpers/request_spec_helper.rb +84 -0
- data/spec/integration/specs/lib/server_spec.rb +474 -0
- data/spec/integration_spec_helper.rb +16 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/test_server.rb +105 -0
- data/spec/unit/agile_proxy/api/applications_spec.rb +102 -0
- data/spec/unit/agile_proxy/api/common_helper.rb +31 -0
- data/spec/unit/agile_proxy/api/recordings_spec.rb +115 -0
- data/spec/unit/agile_proxy/api/request_spec_recordings_spec.rb +119 -0
- data/spec/unit/agile_proxy/api/request_specs_spec.rb +159 -0
- data/spec/unit/agile_proxy/handlers/handler_spec.rb +8 -0
- data/spec/unit/agile_proxy/handlers/proxy_handler_spec.rb +138 -0
- data/spec/unit/agile_proxy/handlers/request_handler_spec.rb +76 -0
- data/spec/unit/agile_proxy/handlers/stub_handler_spec.rb +177 -0
- data/spec/unit/agile_proxy/model/recording_spec.rb +0 -0
- data/spec/unit/agile_proxy/model/request_spec_spec.rb +45 -0
- data/spec/unit/agile_proxy/model/response_spec.rb +38 -0
- data/spec/unit/agile_proxy/server_spec.rb +91 -0
- data/spec/unit/agile_proxy/servers/api_spec.rb +35 -0
- data/spec/unit/agile_proxy/servers/request_spec_direct_spec.rb +51 -0
- data/spec/unit/agile_proxy/servers/request_spec_spec.rb +35 -0
- metadata +736 -0
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative 'common_helper'
|
3
|
+
require 'rack/test'
|
4
|
+
|
5
|
+
describe AgileProxy::Api::RequestSpecs, api_test: true do
|
6
|
+
include Rack::Test::Methods
|
7
|
+
include AgileProxy::Test::Api::Common
|
8
|
+
let(:applications_assoc_class) do
|
9
|
+
Class.new do
|
10
|
+
end
|
11
|
+
end
|
12
|
+
let(:request_spec_assoc_class) do
|
13
|
+
Class.new do
|
14
|
+
def self.destroy_all
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
let(:application_instance) { applications_assoc_class.new }
|
19
|
+
before :each do
|
20
|
+
expect(current_user).to receive(:applications).at_least(:once).and_return(applications_assoc_class)
|
21
|
+
expect(applications_assoc_class).to receive(:where).with(id: '1').at_least(:once).and_return applications_assoc_class
|
22
|
+
expect(applications_assoc_class).to receive(:first).at_least(:once).and_return application_instance
|
23
|
+
expect(application_instance).to receive(:request_specs).and_return request_spec_assoc_class
|
24
|
+
end
|
25
|
+
let(:default_json_spec) { { include: { response: { except: [:created_at, :updated_at] } } } }
|
26
|
+
|
27
|
+
# def mock_collection_data
|
28
|
+
# @__all_request_specs ||= [double('HttpFlexiblePrAgileProxy', :id => 1),double('AgileProxy::ReAgileProxy=> 2),double('AgileProxy::RequestSpAgileProxyeach do |d|
|
29
|
+
# allow(d).to receive(:as_json).with(default_json_spec).and_return({:spec => "Spec #{d.id}"}.as_json)
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
# def mock_collection_association
|
33
|
+
# return @__mock_collection_association if @__mock_collection_association.present?
|
34
|
+
# @__mock_collection_association = double('Mock Association')
|
35
|
+
# @__mock_collection_association.tap do |o|
|
36
|
+
# allow(o).to receive(:total_count).and_return 3
|
37
|
+
# allow(o).to receive(:num_pages).and_return 1
|
38
|
+
# allow(o).to receive(:current_page).and_return 1
|
39
|
+
# allow(o).to receive(:next_page).and_return nil
|
40
|
+
# allow(o).to receive(:prev_page).and_return nil
|
41
|
+
# allow(o).to receive(:page).and_return o
|
42
|
+
# allow(o).to receive(:per).and_return o
|
43
|
+
# allow(o).to receive(:padding).and_return o
|
44
|
+
# end
|
45
|
+
# allow(@__mock_collection_association).to receive(:as_json).with(default_json_spec).and_return(mock_collection_data.as_json(default_json_spec))
|
46
|
+
# allow(@__mock_collection_association).to receive(:count).and_return 3
|
47
|
+
# allow(@__mock_collection_association).to receive(:where) do |options|
|
48
|
+
# if options.key?(:id)
|
49
|
+
# mock_collection_data.select{|r| r.id.to_s == options[:id]}
|
50
|
+
# else
|
51
|
+
# throw "mock_collection_association called with an unknown where clause of #{where.to_json}"
|
52
|
+
# end
|
53
|
+
# end
|
54
|
+
# @__mock_collection_association
|
55
|
+
# end
|
56
|
+
# def created_member(attrs)
|
57
|
+
# double("AgileProxy::RequestSpec", attrs.merge({:as_json => {"spec" => attrs[:spec]}}))
|
58
|
+
# end
|
59
|
+
describe 'GET /users/1/applications/1/request_specs' do
|
60
|
+
let(:request_specs_result) do
|
61
|
+
[{ 'spec' => 'Spec 1' }, { 'spec' => 'Spec 2' }, { 'spec' => 'Spec 3' }]
|
62
|
+
end
|
63
|
+
before :each do
|
64
|
+
expect(request_spec_assoc_class).to receive(:page).and_return request_spec_assoc_class
|
65
|
+
expect(request_spec_assoc_class).to receive(:per).and_return request_spec_assoc_class
|
66
|
+
expect(request_spec_assoc_class).to receive(:padding).and_return request_spec_assoc_class
|
67
|
+
expect(request_spec_assoc_class).to receive(:total_count).and_return 3
|
68
|
+
expect(request_spec_assoc_class).to receive(:num_pages).and_return 1
|
69
|
+
expect(request_spec_assoc_class).to receive(:current_page).and_return 1
|
70
|
+
expect(request_spec_assoc_class).to receive(:next_page).and_return 1
|
71
|
+
expect(request_spec_assoc_class).to receive(:prev_page).and_return 1
|
72
|
+
expect(request_spec_assoc_class).to receive(:count).and_return 3
|
73
|
+
expect(request_spec_assoc_class).to receive(:as_json).with(default_json_spec).and_return request_specs_result
|
74
|
+
end
|
75
|
+
it 'returns a populated array of request specs' do
|
76
|
+
get '/v1/users/1/applications/1/request_specs'
|
77
|
+
expect(last_response.status).to eq(200)
|
78
|
+
expect(JSON.parse(last_response.body)).to eq('request_specs' => [{ 'spec' => 'Spec 1' }, { 'spec' => 'Spec 2' }, { 'spec' => 'Spec 3' }], 'total' => 3)
|
79
|
+
end
|
80
|
+
it 'Should not list items from a different application'
|
81
|
+
it 'Should not list items that belong to a different user'
|
82
|
+
end
|
83
|
+
describe '/users/1/applications/1/request_specs/2' do
|
84
|
+
let(:request_spec_instance) { request_spec_assoc_class.new }
|
85
|
+
let(:persisted_attributes) { { user_id: 1, application_id: 1, url: 'http://www.test.com', http_method: 'GET' } }
|
86
|
+
before :each do
|
87
|
+
expect(request_spec_assoc_class).to receive(:where).with(id: '2').and_return request_spec_assoc_class
|
88
|
+
expect(request_spec_assoc_class).to receive(:first).and_return request_spec_instance
|
89
|
+
expect(request_spec_instance).to receive(:as_json).with(default_json_spec).and_return persisted_attributes
|
90
|
+
end
|
91
|
+
describe 'GET' do
|
92
|
+
it 'returns a single item in json format' do
|
93
|
+
get '/v1/users/1/applications/1/request_specs/2'
|
94
|
+
expect(last_response.status).to eq(200)
|
95
|
+
expect(JSON.parse(last_response.body).symbolize_keys).to eq(persisted_attributes)
|
96
|
+
end
|
97
|
+
it 'Should not find a request spec which does not belong to the application specified'
|
98
|
+
it 'Should not find a request spec which does not belong to the user specified'
|
99
|
+
end
|
100
|
+
describe 'PUT' do
|
101
|
+
it 'Should update an existing item with the attributes given' do
|
102
|
+
expect(current_user).to receive(:id).and_return(1)
|
103
|
+
expect(application_instance).to receive(:id).and_return(1)
|
104
|
+
expect(request_spec_instance).to receive(:update_attributes).with(spec: 'Renamed Spec 2', user_id: 1, application_id: 1).and_return true
|
105
|
+
put '/v1/users/1/applications/1/request_specs/2', { spec: 'Renamed Spec 2' }.to_json, 'CONTENT_TYPE' => 'application/json'
|
106
|
+
expect(last_response.status).to eq 200
|
107
|
+
expect(JSON.parse(last_response.body).symbolize_keys).to eq(persisted_attributes) # Note that the mocked update_attributes doesnt actually update so the original json is expected
|
108
|
+
end
|
109
|
+
end
|
110
|
+
describe 'DELETE' do
|
111
|
+
it 'Should delete the existing item and return it in json form' do
|
112
|
+
expect(request_spec_instance).to receive(:destroy)
|
113
|
+
delete '/v1/users/1/applications/1/request_specs/2'
|
114
|
+
expect(last_response.status).to eq 200
|
115
|
+
expect(JSON.parse(last_response.body).symbolize_keys).to eq(persisted_attributes)
|
116
|
+
end
|
117
|
+
it 'Should not allow deleting of other peoples request specs'
|
118
|
+
end
|
119
|
+
end
|
120
|
+
describe 'POST /users/1/applications/1/request_specs' do
|
121
|
+
let(:request_spec_instance) { request_spec_assoc_class.new }
|
122
|
+
let(:persisted_attributes) { { user_id: 1, application_id: 1, url: 'http://www.test.com', http_method: 'GET', record_requests: true } }
|
123
|
+
before :each do
|
124
|
+
expect(request_spec_instance).to receive(:as_json).with(default_json_spec).and_return(persisted_attributes)
|
125
|
+
expect(current_user).to receive(:id).and_return(1)
|
126
|
+
expect(application_instance).to receive(:id).and_return(1)
|
127
|
+
end
|
128
|
+
it 'Should create a new item with the correct attributes set' do
|
129
|
+
expect(request_spec_assoc_class).to receive(:create).with(spec: 'Spec 4', user_id: 1, application_id: 1).and_return request_spec_instance
|
130
|
+
post '/v1/users/1/applications/1/request_specs', { spec: 'Spec 4' }.to_json, 'CONTENT_TYPE' => 'application/json'
|
131
|
+
expect([200, 201]).to include(last_response.status)
|
132
|
+
expect(JSON.parse(last_response.body).symbolize_keys).to eq(persisted_attributes)
|
133
|
+
end
|
134
|
+
it 'Should allow creating of a response object also' do
|
135
|
+
response_attributes = { name: 'Test Response', content: '<h1>Hello World</h1>', content_type: 'text/html', status_code: 200, headers: '{}', is_template: false }
|
136
|
+
expect(request_spec_assoc_class).to receive(:create).with(spec: 'Spec 4', user_id: 1, application_id: 1, response_attributes: Hashie::Mash.new(response_attributes)).and_return request_spec_instance
|
137
|
+
post '/v1/users/1/applications/1/request_specs', { spec: 'Spec 4', response: response_attributes }.to_json, 'CONTENT_TYPE' => 'application/json'
|
138
|
+
expect([200, 201]).to include(last_response.status)
|
139
|
+
expect(JSON.parse(last_response.body).symbolize_keys).to eq(persisted_attributes)
|
140
|
+
end
|
141
|
+
it 'Should inform the router of the new entry'
|
142
|
+
it 'Should not allow the user to specify the user_id to prevent creating for a different user'
|
143
|
+
it 'Should not allow setting of an application_id which doesnt belong to the current user'
|
144
|
+
end
|
145
|
+
describe 'DELETE /users/1/applications/1/request_specs' do
|
146
|
+
before :each do
|
147
|
+
expect(request_spec_assoc_class).to receive(:destroy_all)
|
148
|
+
end
|
149
|
+
it 'Should delete all request specs for the users application' do
|
150
|
+
delete '/v1/users/1/applications/1/request_specs'
|
151
|
+
expect(JSON.parse(last_response.body).symbolize_keys).to eq(request_specs: [], total: 0)
|
152
|
+
|
153
|
+
end
|
154
|
+
end
|
155
|
+
it 'Should not find other peoples request specs to update'
|
156
|
+
it 'Should not allow updates of the user_id to prevent changing ownership'
|
157
|
+
it 'Should not allow updates of the application_id to prevent changing ownership via the application'
|
158
|
+
it 'Should inform the router of the update if the spec or response has changed'
|
159
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AgileProxy::Handler do
|
4
|
+
let(:handler) { Class.new { include AgileProxy::Handler }.new }
|
5
|
+
it '#handle_request raises an error if not overridden' do
|
6
|
+
expect(handler.call(nil)).to eql([500, {}, 'The handler has not overridden the handle_request method!'])
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AgileProxy::ProxyHandler do
|
4
|
+
subject { AgileProxy::ProxyHandler.new }
|
5
|
+
let(:request) do
|
6
|
+
ActionDispatch::Request.new to_rack_env(
|
7
|
+
method: 'post',
|
8
|
+
url: 'http://example.test:8080/index?some=param',
|
9
|
+
headers: { 'Accept-Encoding' => 'gzip',
|
10
|
+
'Cache-Control' => 'no-cache' },
|
11
|
+
body: 'Some body'
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
def request_for_url(url)
|
16
|
+
ActionDispatch::Request.new to_rack_env(
|
17
|
+
method: 'post',
|
18
|
+
url: url,
|
19
|
+
headers: { 'Accept-Encoding' => 'gzip',
|
20
|
+
'Cache-Control' => 'no-cache' },
|
21
|
+
body: 'Some body'
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#handles_request?' do
|
26
|
+
context 'with non-whitelisted requests enabled' do
|
27
|
+
before do
|
28
|
+
expect(AgileProxy.config).to receive(:non_whitelisted_requests_disabled).and_return(false)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
context 'with non-whitelisted requests disabled' do
|
32
|
+
before do
|
33
|
+
expect(AgileProxy.config).to receive(:non_whitelisted_requests_disabled).and_return(true)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'does not handle requests that are not white or black listed' do
|
37
|
+
expect(subject.send(:handles_request?, request)).to be_falsy
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'a whitelisted host' do
|
41
|
+
context 'with a blacklisted path' do
|
42
|
+
before do
|
43
|
+
expect(AgileProxy.config).to receive(:path_blacklist) { ['/index'] }
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'does not handle requests for blacklisted paths' do
|
47
|
+
req = request_for_url 'http://example.test:8080/index?some=param'
|
48
|
+
expect(subject.send(:handles_request?, req)).to be_falsy
|
49
|
+
end
|
50
|
+
end
|
51
|
+
context 'without a port' do
|
52
|
+
before do
|
53
|
+
expect(AgileProxy.config).to receive(:whitelist) { ['example.test'] }
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'handles requests for the host without a port' do
|
57
|
+
req = request_for_url 'http://example.test'
|
58
|
+
expect(subject.send(:handles_request?, req)).to be_truthy
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'handles requests for the host with a port' do
|
62
|
+
req = request_for_url 'http://example.test:8080'
|
63
|
+
expect(subject.send(:handles_request?, req)).to be_truthy
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'with a port' do
|
68
|
+
before do
|
69
|
+
expect(AgileProxy.config).to receive(:whitelist) { ['example.test:8080'] }
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'does not handle requests whitelisted for a specific port' do
|
73
|
+
req = request_for_url 'http://example.test'
|
74
|
+
expect(subject.send(:handles_request?, req)).to be_falsy
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'handles requests for the host with a port' do
|
78
|
+
req = request_for_url 'http://example.test:8080'
|
79
|
+
expect(subject.send(:handles_request?, req)).to be_truthy
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#call' do
|
87
|
+
it 'returns nil if it does not handle the request' do
|
88
|
+
expect(subject).to receive(:handles_request?).and_return(false)
|
89
|
+
expect(subject.call(request.env)).to eql [404, {}, ['Not proxied']]
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'with a handled request' do
|
93
|
+
let(:response_header) do
|
94
|
+
header = Struct.new(:status, :raw).new
|
95
|
+
header.status = 200
|
96
|
+
header.raw = {}
|
97
|
+
header
|
98
|
+
end
|
99
|
+
|
100
|
+
let(:em_response) { double('response') }
|
101
|
+
let(:em_request) do
|
102
|
+
double('EM::HttpRequest', error: nil, response: em_response, response_header: response_header)
|
103
|
+
end
|
104
|
+
|
105
|
+
before do
|
106
|
+
allow(subject).to receive(:handles_request?).and_return(true)
|
107
|
+
allow(em_response).to receive(:force_encoding).and_return('The response body')
|
108
|
+
allow(EventMachine::HttpRequest).to receive(:new).and_return(em_request)
|
109
|
+
expect(em_request).to receive(:post).and_return(em_request)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'Should pass through a not allowed response' do
|
113
|
+
allow(response_header).to receive(:status).and_return(503)
|
114
|
+
expect(subject.call(request.env)).to eql [503, { 'Connection' => 'close', 'Cache-Control' => 'max-age=3600' }, ['The response body']]
|
115
|
+
end
|
116
|
+
it 'returns any error in the response' do
|
117
|
+
allow(em_request).to receive(:error).and_return('ERROR!')
|
118
|
+
expect(subject.call(request.env)).to eql([500, {}, ["Request to #{request.url} failed with error: ERROR!"]])
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'returns a hashed response if the request succeeds' do
|
122
|
+
expect(subject.call(request.env)).to eql([200, { 'Connection' => 'close', 'Cache-Control' => 'max-age=3600' }, ['The response body']])
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'returns nil if both the error and response are for some reason nil' do
|
126
|
+
allow(em_request).to receive(:response).and_return(nil)
|
127
|
+
expect(subject.call(request.env)).to eql [404, {}, ['Not proxied']]
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'uses the timeouts defined in configuration' do
|
131
|
+
allow(AgileProxy.config).to receive(:proxied_request_inactivity_timeout).and_return(42)
|
132
|
+
allow(AgileProxy.config).to receive(:proxied_request_connect_timeout).and_return(24)
|
133
|
+
expect(EventMachine::HttpRequest).to receive(:new).with(request.url, inactivity_timeout: 42, connect_timeout: 24)
|
134
|
+
subject.call(request.env)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AgileProxy::RequestHandler do
|
4
|
+
subject { AgileProxy::RequestHandler.new }
|
5
|
+
|
6
|
+
it 'implements Handler' do
|
7
|
+
expect(subject).to be_a AgileProxy::Handler
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'with stubbed handlers' do
|
11
|
+
let(:env) { to_rack_env(url: 'http://dummy.host.com/index.html').merge('agile_proxy.request_spec' => mock_request_spec) }
|
12
|
+
let(:stub_handler) { Class.new }
|
13
|
+
let(:proxy_handler) { Class.new }
|
14
|
+
let(:application_class) { Class.new }
|
15
|
+
let(:recordings_class) { Class.new }
|
16
|
+
let(:rack_builder_class) {Class.new}
|
17
|
+
let(:get_only_cache_class) {Class.new}
|
18
|
+
let(:application) { double('Application', record_requests: false, recordings: recordings_class) }
|
19
|
+
let(:mock_request_spec) {double('RequestSpec', id: 8, record_requests: false)}
|
20
|
+
|
21
|
+
before do
|
22
|
+
stub_const 'AgileProxy::StubHandler', stub_handler
|
23
|
+
stub_const 'AgileProxy::ProxyHandler', proxy_handler
|
24
|
+
stub_const 'AgileProxy::Application', application_class
|
25
|
+
stub_const '::Rack::Builder', rack_builder_class
|
26
|
+
stub_const 'AgileProxy::Rack::GetOnlyCache', get_only_cache_class
|
27
|
+
allow(application_class).to receive(:where).and_return [application]
|
28
|
+
#Make the rack builder just pass through whatever is passed to 'run' - to avoid the caching middleware etc..
|
29
|
+
rack_builder_instance = rack_builder_class.new
|
30
|
+
allow(rack_builder_class).to receive(:new) do |&blk|
|
31
|
+
rack_builder_instance.instance_eval(&blk)
|
32
|
+
end
|
33
|
+
allow(rack_builder_instance).to receive(:use).with(get_only_cache_class)
|
34
|
+
allow(rack_builder_instance).to receive(:run) do |app|
|
35
|
+
app
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#call' do
|
40
|
+
it 'returns error 500 if no handlers handle the request' do
|
41
|
+
expect_any_instance_of(stub_handler).to receive(:call).and_return [404, {}, 'It didnt work']
|
42
|
+
expect_any_instance_of(proxy_handler).to receive(:call).and_return [404, {}, 'It didnt work']
|
43
|
+
expect(subject.call(env)).to start_with [500, {}]
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'returns 200 immediately if the stub handler handles the request' do
|
47
|
+
expect_any_instance_of(stub_handler).to receive(:call).with(env).and_return [200, {}, 'Some data']
|
48
|
+
expect_any_instance_of(proxy_handler).to_not receive(:call)
|
49
|
+
expect(subject.call(env)).to eql [200, {}, 'Some data']
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'returns true if the proxy handler handles the request' do
|
53
|
+
expect_any_instance_of(stub_handler).to receive(:call).with(env).and_return [404, {}, 'Irrelevant']
|
54
|
+
expect_any_instance_of(proxy_handler).to receive(:call).with(env).and_return [200, {}, 'Some data']
|
55
|
+
expect(subject.call(env)).to eql [200, {}, 'Some data']
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'Calls application.recordings.create with a reference to the stub if record_requests is true on the application' do
|
59
|
+
allow(application).to receive(:record_requests).and_return true
|
60
|
+
expect(application.recordings).to receive(:create).with(a_hash_including request_spec_id: 8)
|
61
|
+
expect_any_instance_of(stub_handler).to receive(:call).with(env).and_return [200, {}, 'Some data']
|
62
|
+
expect_any_instance_of(proxy_handler).to_not receive(:call)
|
63
|
+
expect(subject.call(env)).to eql [200, {}, 'Some data']
|
64
|
+
end
|
65
|
+
it 'Calls application.recordings.create with a reference to the stub if record_requests is true on the request spec' do
|
66
|
+
allow(mock_request_spec).to receive(:record_requests).and_return true
|
67
|
+
expect(application.recordings).to receive(:create).with(a_hash_including request_spec_id: 8)
|
68
|
+
expect_any_instance_of(stub_handler).to receive(:call).with(env).and_return [200, {}, 'Some data']
|
69
|
+
expect_any_instance_of(proxy_handler).to_not receive(:call)
|
70
|
+
expect(subject.call(env)).to eql [200, {}, 'Some data']
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AgileProxy::StubHandler do
|
4
|
+
#Mock the content length middleware as we are not testing if that works
|
5
|
+
let(:rack_content_length_class) do
|
6
|
+
Class.new do
|
7
|
+
def initialize(app)
|
8
|
+
@app = app
|
9
|
+
end
|
10
|
+
def call(env)
|
11
|
+
@app.call(env)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
let(:route_not_found_response) { [404, { 'X-Cascade' => 'pass' }, ['Not Found']] }
|
16
|
+
let(:handler) { AgileProxy::StubHandler.new }
|
17
|
+
let(:request) do
|
18
|
+
request_for(
|
19
|
+
method: 'GET',
|
20
|
+
url: 'http://example.test:8080/index?some=param',
|
21
|
+
headers: { 'Accept-Encoding' => 'gzip',
|
22
|
+
'Cache-Control' => 'no-cache' }
|
23
|
+
)
|
24
|
+
end
|
25
|
+
let(:application_class) { Class.new }
|
26
|
+
let(:request_spec_class) { Class.new }
|
27
|
+
let(:application) { application_class.new }
|
28
|
+
|
29
|
+
def request_for(options)
|
30
|
+
request = ActionDispatch::Request.new(to_rack_env(options))
|
31
|
+
request.params
|
32
|
+
request
|
33
|
+
end
|
34
|
+
|
35
|
+
before :each do
|
36
|
+
stub_const('AgileProxy::Application', application_class)
|
37
|
+
stub_const('Rack::ContentLength', rack_content_length_class)
|
38
|
+
allow(application_class).to receive(:where).and_return application_class
|
39
|
+
allow(application_class).to receive(:first).and_return application
|
40
|
+
allow(application).to receive(:request_specs).and_return request_spec_class
|
41
|
+
end
|
42
|
+
describe 'With find_stub mocked' do
|
43
|
+
|
44
|
+
describe '#handle_request' do
|
45
|
+
it 'returns 404 if the request is not stubbed' do
|
46
|
+
stub = double('stub', http_method: 'GET', url: 'http://example.test:8080/index', conditions_json: {}, call: [404, {}, 'Not found'], id: 5)
|
47
|
+
expect(request_spec_class).to receive(:where).and_return double('association', all: [stub])
|
48
|
+
expect(handler.call(to_rack_env(url: 'http://example.test:8080/index'))).to eql [404, {}, 'Not found']
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'returns a response hash if the request is stubbed' do
|
52
|
+
stub = double('stub', http_method: 'GET', url: 'http://example.test:8080/index', conditions_json: {}, call: [200, { 'Content-Type' => 'application/json' }, 'Some content'], id: 5)
|
53
|
+
expect(request_spec_class).to receive(:where).and_return double('association', all: [stub])
|
54
|
+
expect(handler.call(request.env)).to eql([200, { 'Content-Type' => 'application/json' }, 'Some content'])
|
55
|
+
end
|
56
|
+
it 'Passes on the correct parameters to the stub call method' do
|
57
|
+
stub = double('stub', http_method: 'GET', url: 'http://example.test:8080/index', conditions_json: {}, id: 5)
|
58
|
+
expect(request_spec_class).to receive(:where).and_return double('association', all: [stub])
|
59
|
+
body = request.body.read
|
60
|
+
request.body.rewind
|
61
|
+
expect(stub).to receive(:call).with({ some: 'param' }, { 'Accept-Encoding' => 'gzip', 'Cache-Control' => 'no-cache' }, body).and_return([200, { 'Content-Type' => 'application/json' }, 'Some Content'])
|
62
|
+
expect(handler.call(request.env)).to eql([200, { 'Content-Type' => 'application/json' }, 'Some Content'])
|
63
|
+
|
64
|
+
end
|
65
|
+
it 'should store the id of the request spec in the rack environment when call is called' do
|
66
|
+
stub = double('stub', http_method: 'GET', url: 'http://example.test:8080/index', conditions_json: {}, id: 5)
|
67
|
+
expect(request_spec_class).to receive(:where).and_return double('association', all: [stub])
|
68
|
+
body = request.body.read
|
69
|
+
request.body.rewind
|
70
|
+
expect(stub).to receive(:call).with({ some: 'param' }, { 'Accept-Encoding' => 'gzip', 'Cache-Control' => 'no-cache' }, body).and_return([200, { 'Content-Type' => 'application/json' }, 'Some Content'])
|
71
|
+
expect(handler.call(request.env)).to eql([200, { 'Content-Type' => 'application/json' }, 'Some Content'])
|
72
|
+
expect(request.env).to include('agile_proxy.request_spec' => stub)
|
73
|
+
|
74
|
+
end
|
75
|
+
describe 'Routing patterns' do
|
76
|
+
describe 'With a simple GET match on the root of a domain' do
|
77
|
+
let(:request_stub) { double 'stub', url: 'http://example.com', http_method: 'GET', conditions_json: {}, id: 5 }
|
78
|
+
before :each do
|
79
|
+
allow(request_spec_class).to receive(:where).with('url LIKE ?', 'http://example.com%').and_return double('association', all: [request_stub])
|
80
|
+
allow(request_spec_class).to receive(:where).with('url LIKE ?', 'http://subdomain.example.com%').and_return double('association', all: [])
|
81
|
+
allow(request_stub).to receive(:call).and_return([200, {}, ''])
|
82
|
+
end
|
83
|
+
it 'Should match with a get on the same domain but not with a post or a different domain' do
|
84
|
+
expect(handler.call(request_for(url: 'http://example.com').env)).to eql([200, {}, ''])
|
85
|
+
expect(handler.call(request_for(url: 'http://example.com/').env)).to eql([200, {}, ''])
|
86
|
+
expect(handler.call(request_for(url: 'http://example.com/', method: 'POST').env)).to contain_exactly *route_not_found_response
|
87
|
+
expect(handler.call(request_for(url: 'http://subdomain.example.com/').env)).to contain_exactly *route_not_found_response
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
describe 'With a simple GET match inside a domain' do
|
92
|
+
let(:request_stub) { double 'stub for simple get inside a domain', url: 'http://example.com/index', http_method: 'GET', conditions_json: {}, id: 5 }
|
93
|
+
before :each do
|
94
|
+
allow(request_spec_class).to receive(:where).with('url LIKE ?', 'http://example.com%').and_return double('association', all: [request_stub])
|
95
|
+
allow(request_spec_class).to receive(:where).with('url LIKE ?', 'http://subdomain.example.com%').and_return double('association', all: [])
|
96
|
+
expect(request_stub).to receive(:call).and_return([200, {}, ''])
|
97
|
+
end
|
98
|
+
it 'Should match with a get on the same domain but not with a post or a different domain' do
|
99
|
+
expect(handler.call(request_for(url: 'http://example.com/index').env)).to eql([200, {}, ''])
|
100
|
+
expect(handler.call(request_for(method: 'POST', url: 'http://example.com/index').env)).to contain_exactly *route_not_found_response
|
101
|
+
expect(handler.call(request_for(url: 'http://subdomain.example.com/index').env)).to contain_exactly *route_not_found_response
|
102
|
+
expect(handler.call(request_for(url: 'http://example.com/').env)).to contain_exactly *route_not_found_response
|
103
|
+
expect(handler.call(request_for(url: 'http://example.com').env)).to contain_exactly *route_not_found_response
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
describe 'With a simple POST match on the root of a domain' do
|
108
|
+
let(:request_stub) { double 'stub', url: 'http://example.com', http_method: 'POST', conditions_json: {}, id: 5 }
|
109
|
+
before :each do
|
110
|
+
allow(request_spec_class).to receive(:where).with('url LIKE ?', 'http://example.com%').and_return double('association', all: [request_stub])
|
111
|
+
allow(request_spec_class).to receive(:where).with('url LIKE ?', 'http://subdomain.example.com%').and_return double('association', all: [])
|
112
|
+
allow(request_stub).to receive(:call).and_return([200, {}, ''])
|
113
|
+
end
|
114
|
+
it 'Should match with a post on the same domain but not with a get or a post on a different domain' do
|
115
|
+
expect(handler.call(request_for(method: 'POST', url: 'http://example.com').env)).to eql([200, {}, ''])
|
116
|
+
expect(handler.call(request_for(method: 'POST', url: 'http://example.com/').env)).to eql([200, {}, ''])
|
117
|
+
expect(handler.call(request_for(method: 'POST', url: 'http://example.com/', headers: {'Content-Type' => 'text/plain'}, body: "a=1\nb=2\nc=3").env)).to eql([200, {}, ''])
|
118
|
+
expect(handler.call(request_for(url: 'http://example.com/').env)).to contain_exactly *route_not_found_response
|
119
|
+
expect(handler.call(request_for(method: 'POST', url: 'http://subdomain.example.com/').env)).to contain_exactly *route_not_found_response
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
describe 'With a more complex route with conditions inside a domain' do
|
124
|
+
let(:request_stub) { double 'stub for complex route inside a domain', url: 'http://example.com/users/:user_id/index', http_method: 'GET', conditions_json: { user_id: '1' }, id: 5 }
|
125
|
+
before :each do
|
126
|
+
allow(request_spec_class).to receive(:where).with('url LIKE ?', 'http://example.com%').and_return double('association', all: [request_stub])
|
127
|
+
expect(request_stub).to receive(:call).and_return([200, {}, ''])
|
128
|
+
end
|
129
|
+
it 'Should match with a get on the same domain but not with a post or a different domain' do
|
130
|
+
expect(handler.call(request_for(url: 'http://example.com/users/1/index').env)).to eql([200, {}, ''])
|
131
|
+
expect(handler.call(request_for(url: 'http://example.com/users/2/index').env)).to contain_exactly *route_not_found_response
|
132
|
+
expect(handler.call(request_for(method: 'POST', url: 'http://example.com/users/1/index').env)).to contain_exactly *route_not_found_response
|
133
|
+
expect(handler.call(request_for(url: 'http://example.com/users/1/').env)).to contain_exactly *route_not_found_response
|
134
|
+
expect(handler.call(request_for(url: 'http://example.com/users/1').env)).to contain_exactly *route_not_found_response
|
135
|
+
end
|
136
|
+
end
|
137
|
+
describe 'With a more complex route with conditions including query params inside a domain' do
|
138
|
+
let(:request_stub) { double 'stub for complex route inside a domain', url: 'http://example.com/users/:user_id/index', http_method: 'GET', conditions_json: { user_id: '1', extra_1: 'extra_1', extra_2: 'extra_2' }, id: 5 }
|
139
|
+
before :each do
|
140
|
+
allow(request_spec_class).to receive(:where).with('url LIKE ?', 'http://example.com%').and_return double('association', all: [request_stub])
|
141
|
+
allow(request_stub).to receive(:call).and_return([200, {}, ''])
|
142
|
+
end
|
143
|
+
it 'Should match with a get on the same domain but not with a post or a different domain' do
|
144
|
+
expect(handler.call(request_for(url: 'http://example.com/users/1/index?extra_1=extra_1&extra_2=extra_2').env)).to eql([200, {}, ''])
|
145
|
+
expect(handler.call(request_for(url: 'http://example.com/users/1/index?some_other=2&extra_1=extra_1&extra_2=extra_2').env)).to eql([200, {}, ''])
|
146
|
+
expect(handler.call(request_for(url: 'http://example.com/users/2/index').env)).to contain_exactly *route_not_found_response
|
147
|
+
expect(handler.call(request_for(method: 'POST', url: 'http://example.com/users/1/index').env)).to contain_exactly *route_not_found_response
|
148
|
+
expect(handler.call(request_for(url: 'http://example.com/users/1/').env)).to contain_exactly *route_not_found_response
|
149
|
+
expect(handler.call(request_for(url: 'http://example.com/users/1').env)).to contain_exactly *route_not_found_response
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# it 'should match regexps' do
|
154
|
+
# expect(AgileProxy::RequestSpec.new(:url => "http:\/\/.+\.com", :method => :post, :regex => true).
|
155
|
+
# matches?('POST', 'http://example.com')).to be
|
156
|
+
# expect(AgileProxy::RequestSpec.new(:url => "http:\/\/.+\.co\.uk", :method => :get, :regex => true).
|
157
|
+
# matches?('GET', 'http://example.com')).to_not be
|
158
|
+
# end
|
159
|
+
#
|
160
|
+
# it 'should match up to but not including query strings' do
|
161
|
+
# stub = AgileProxy::RequestSpec.new(:url => 'http://example.com/foo/bar/')
|
162
|
+
# expect(stub.matches?('GET', 'http://example.com/foo/')).to_not be
|
163
|
+
# expect(stub.matches?('GET', 'http://example.com/foo/bar/')).to be
|
164
|
+
# expect(stub.matches?('GET', 'http://example.com/foo/bar/?baz=bap')).to be
|
165
|
+
# end
|
166
|
+
# it 'Should match routes using pattern matching' do
|
167
|
+
# stub = AgileProxy::RequestSpec.new(:url => "http://example.com/users/:user_id/application/:application_id")
|
168
|
+
# expect(stub.matches?('GET', 'http://example.com/users/user_id/application/application_id')).to be
|
169
|
+
# expect(stub.matches?('GET', 'http://example.com/users/user_id/somethingelse/application_id')).not_to be
|
170
|
+
# end
|
171
|
+
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|