agile-proxy 0.1.0
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 +8 -0
- data/.rspec +2 -0
- data/.rubocop.yml +36 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +267 -0
- data/Guardfile +20 -0
- data/LICENSE +22 -0
- data/README.md +93 -0
- data/Rakefile +13 -0
- data/agile-proxy.gemspec +50 -0
- data/assets/index.html +39 -0
- data/assets/ui/app/HttpFlexibleProxyApi.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 +10 -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 +113 -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/schema.rb +78 -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_specs.rb +85 -0
- data/lib/agile_proxy/api/root.rb +41 -0
- data/lib/agile_proxy/config.rb +63 -0
- data/lib/agile_proxy/handlers/handler.rb +43 -0
- data/lib/agile_proxy/handlers/proxy_handler.rb +110 -0
- data/lib/agile_proxy/handlers/request_handler.rb +57 -0
- data/lib/agile_proxy/handlers/stub_handler.rb +113 -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 +16 -0
- data/lib/agile_proxy/model/request_spec.rb +47 -0
- data/lib/agile_proxy/model/response.rb +56 -0
- data/lib/agile_proxy/model/user.rb +17 -0
- data/lib/agile_proxy/proxy_connection.rb +113 -0
- data/lib/agile_proxy/route.rb +106 -0
- data/lib/agile_proxy/router.rb +99 -0
- data/lib/agile_proxy/server.rb +85 -0
- data/lib/agile_proxy/servers/api.rb +41 -0
- data/lib/agile_proxy/servers/request_spec.rb +30 -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/test-server.crt +15 -0
- data/spec/fixtures/test-server.key +15 -0
- data/spec/integration/helpers/request_spec_helper.rb +60 -0
- data/spec/integration/specs/lib/server_spec.rb +407 -0
- data/spec/integration_spec_helper.rb +18 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/test_server.rb +75 -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_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 +55 -0
- data/spec/unit/agile_proxy/handlers/stub_handler_spec.rb +154 -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 +88 -0
- data/spec/unit/agile_proxy/servers/api_spec.rb +31 -0
- data/spec/unit/agile_proxy/servers/request_spec_spec.rb +32 -0
- metadata +618 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
require 'active_record'
|
|
2
|
+
require 'yaml'
|
|
3
|
+
require 'cgi'
|
|
4
|
+
require 'uri'
|
|
5
|
+
require 'eventmachine'
|
|
6
|
+
require 'thin'
|
|
7
|
+
require 'grape'
|
|
8
|
+
require 'agile_proxy/api/root'
|
|
9
|
+
require 'agile_proxy/servers/api'
|
|
10
|
+
require 'agile_proxy/servers/request_spec'
|
|
11
|
+
|
|
12
|
+
module AgileProxy
|
|
13
|
+
#
|
|
14
|
+
# This class is responsible for controlling the underlying proxy and web servers
|
|
15
|
+
#
|
|
16
|
+
class Server
|
|
17
|
+
ROOT = File.expand_path '../../', File.dirname(__FILE__)
|
|
18
|
+
extend Forwardable
|
|
19
|
+
attr_reader :request_handler
|
|
20
|
+
|
|
21
|
+
def_delegators :request_handler, :reset_cache, :restore_cache, :handle_request
|
|
22
|
+
|
|
23
|
+
def initialize
|
|
24
|
+
environment = AgileProxy.config.environment
|
|
25
|
+
dbconfig = YAML.load(File.read(AgileProxy.config.database_config_file)).with_indifferent_access
|
|
26
|
+
ActiveRecord::Base.configurations = dbconfig
|
|
27
|
+
ActiveRecord::Base.establish_connection dbconfig[environment.to_s]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Starts the proxy and web servers
|
|
31
|
+
def start
|
|
32
|
+
main_loop
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# The url that the proxy server is running on
|
|
36
|
+
# @return [String] The URL
|
|
37
|
+
def url
|
|
38
|
+
"http://#{host}:#{port}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# The url that the web server can be accessed from
|
|
42
|
+
# @return [String] The URL
|
|
43
|
+
def webserver_url
|
|
44
|
+
"http://#{webserver_host}:#{webserver_port}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# The host that the proxy server is running on
|
|
48
|
+
# @return [String] The host
|
|
49
|
+
def host
|
|
50
|
+
'localhost'
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# The port that the proxy server is running on
|
|
54
|
+
# @return [String] The port
|
|
55
|
+
def port
|
|
56
|
+
@request_spec_server.port
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# The host that the webserver is running on
|
|
60
|
+
# @return [String] The host
|
|
61
|
+
def webserver_host
|
|
62
|
+
AgileProxy.config.webserver_host
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# The port that the webserver is running on
|
|
66
|
+
# @return [String] The port
|
|
67
|
+
def webserver_port
|
|
68
|
+
AgileProxy.config.webserver_port
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
protected
|
|
72
|
+
|
|
73
|
+
def main_loop
|
|
74
|
+
EM.run do
|
|
75
|
+
EM.error_handler do |e|
|
|
76
|
+
puts e.class.name, e
|
|
77
|
+
puts e.backtrace.join("\n")
|
|
78
|
+
end
|
|
79
|
+
AgileProxy::Servers::Api.start(webserver_host, webserver_port)
|
|
80
|
+
@request_spec_server = AgileProxy::Servers::RequestSpec.start
|
|
81
|
+
AgileProxy.log(:info, "agile-proxy: Proxy listening on #{url} and webserver listening on #{webserver_url}")
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'rack'
|
|
2
|
+
require 'thin'
|
|
3
|
+
require 'grape'
|
|
4
|
+
require 'agile_proxy/api/root'
|
|
5
|
+
|
|
6
|
+
module AgileProxy
|
|
7
|
+
module Servers
|
|
8
|
+
#
|
|
9
|
+
# The API Server
|
|
10
|
+
#
|
|
11
|
+
# This server is a RACK server responsible for providing access to the system
|
|
12
|
+
# using REST requests.
|
|
13
|
+
# This allows remote programming of the proxy using either a client adapter or the built in user interface
|
|
14
|
+
module Api
|
|
15
|
+
ROOT = File.expand_path '../../../', File.dirname(__FILE__)
|
|
16
|
+
class << self
|
|
17
|
+
#
|
|
18
|
+
# Starts the webserver on the given host and port
|
|
19
|
+
# @param webserver_host [String] The host for the server to run on
|
|
20
|
+
# @param webserver_port [Integer] The port for the server to run on
|
|
21
|
+
def start(webserver_host, webserver_port)
|
|
22
|
+
# The sinatra web server
|
|
23
|
+
dispatch = Rack::Builder.app do
|
|
24
|
+
use Rack::Static, root: File.join(ROOT, 'assets'), urls: ['/ui'], index: 'index.html'
|
|
25
|
+
map '/api' do
|
|
26
|
+
run ::AgileProxy::Api::Root.new
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
# Start the web server.
|
|
30
|
+
::Rack::Server.start(
|
|
31
|
+
app: dispatch,
|
|
32
|
+
server: 'thin',
|
|
33
|
+
Host: webserver_host,
|
|
34
|
+
Port: webserver_port,
|
|
35
|
+
signals: false
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'eventmachine'
|
|
2
|
+
module AgileProxy
|
|
3
|
+
module Servers
|
|
4
|
+
#
|
|
5
|
+
# The 'Request Spec' server
|
|
6
|
+
# This server is responsible for handling or passing through a request, depending
|
|
7
|
+
# on if it has a matching 'Request Specification'
|
|
8
|
+
class RequestSpec
|
|
9
|
+
# Starts the server
|
|
10
|
+
def self.start
|
|
11
|
+
new.start
|
|
12
|
+
end
|
|
13
|
+
def initialize
|
|
14
|
+
@request_handler = AgileProxy::RequestHandler.new
|
|
15
|
+
end
|
|
16
|
+
# Starts the server
|
|
17
|
+
def start
|
|
18
|
+
@signature = EM.start_server('127.0.0.1', AgileProxy.config.proxy_port, ProxyConnection) do |p|
|
|
19
|
+
p.handler = @request_handler
|
|
20
|
+
end
|
|
21
|
+
self
|
|
22
|
+
end
|
|
23
|
+
# The port the server is running on
|
|
24
|
+
# @return [Integer] The port the server is running on
|
|
25
|
+
def port
|
|
26
|
+
Socket.unpack_sockaddr_in(EM.get_sockname(@signature)).first
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
data/load_proxy.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
var proxy, contracts;
|
|
2
|
+
proxy = require('flexible_proxy_client');
|
|
3
|
+
debugger;
|
|
4
|
+
contracts = [];
|
|
5
|
+
for (i=0; i< 100; i=i+1) {
|
|
6
|
+
contracts.push({
|
|
7
|
+
"created_at": "2014-10-21T09:30:45.217Z",
|
|
8
|
+
"customer_company_id": "1",
|
|
9
|
+
"customer_contact_id": "1",
|
|
10
|
+
"daily_rate_currency": "GBP",
|
|
11
|
+
"daily_rate_pennies": 10000,
|
|
12
|
+
"duration": 2678400,
|
|
13
|
+
"end_date": "2013-01-31T00:00:00.000Z",
|
|
14
|
+
"fq_name": "My First Contract",
|
|
15
|
+
"id": "544627c51fedb0383c0000" + i,
|
|
16
|
+
"name": "My Contract No " + (i + 1),
|
|
17
|
+
"owner_company_id": "1",
|
|
18
|
+
"owner_id": "544627c51fedb0383c000092",
|
|
19
|
+
"start_date": "2013-01-01T00:00:00.000Z",
|
|
20
|
+
"status": "accepted",
|
|
21
|
+
"supplier_company_id": "1",
|
|
22
|
+
"updated_at": "2014-10-21T09:30:45.217Z",
|
|
23
|
+
"value_currency": "GBP",
|
|
24
|
+
"value_pennies": 100000,
|
|
25
|
+
"notes": [
|
|
26
|
+
|
|
27
|
+
]
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
proxy.define([
|
|
31
|
+
proxy.stub('http://localhost:9000/api/v1/contracts.json').andReturn({json: {
|
|
32
|
+
"contracts": contracts,
|
|
33
|
+
"total": contracts.length,
|
|
34
|
+
"success": true
|
|
35
|
+
}}),
|
|
36
|
+
proxy.stub('http://localhost:35729/livereload.js').andReturn({
|
|
37
|
+
json: {}
|
|
38
|
+
})
|
|
39
|
+
], function () {console.log("YEP")});
|
data/log/.gitkeep
ADDED
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module AgileProxy
|
|
2
|
+
module Test
|
|
3
|
+
# Common helpers for the test suite
|
|
4
|
+
module Common
|
|
5
|
+
def to_rack_env(opts = {})
|
|
6
|
+
fake_input_buffer = StringIO.new(opts[:body] || '')
|
|
7
|
+
fake_error_buffer = StringIO.new
|
|
8
|
+
url_parsed = URI.parse(opts[:url])
|
|
9
|
+
env = {
|
|
10
|
+
'rack.input' => Rack::Lint::InputWrapper.new(fake_input_buffer),
|
|
11
|
+
'rack.errors' => Rack::Lint::ErrorWrapper.new(fake_error_buffer),
|
|
12
|
+
'REQUEST_METHOD' => (opts[:method] || 'GET').upcase,
|
|
13
|
+
'REQUEST_PATH' => url_parsed.path,
|
|
14
|
+
'PATH_INFO' => url_parsed.path,
|
|
15
|
+
'QUERY_STRING' => url_parsed.query || '',
|
|
16
|
+
'REQUEST_URI' => url_parsed.path + (url_parsed.query.nil? ? '' : url_parsed.query),
|
|
17
|
+
'rack.url_scheme' => url_parsed.scheme,
|
|
18
|
+
'CONTENT_LENGTH' => (opts[:body] || '').length,
|
|
19
|
+
'SERVER_NAME' => url_parsed.host,
|
|
20
|
+
'SERVER_PORT' => url_parsed.port
|
|
21
|
+
}
|
|
22
|
+
(opts[:headers] || {}).each do |name, value|
|
|
23
|
+
converted_name = 'HTTP_' + (name.gsub(/-/, '_').upcase)
|
|
24
|
+
env[converted_name] = value
|
|
25
|
+
end
|
|
26
|
+
env['CONTENT_TYPE'] = env.delete('HTTP_CONTENT_TYPE') if env.key?('HTTP_CONTENT_TYPE')
|
|
27
|
+
env['CONTENT_LENGTH'] = env.delete('HTTP_CONTENT_LENGTH') if env.key?('HTTP_CONTENT_LENGTH')
|
|
28
|
+
env
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIICazCCAdQCCQD9MxXmqmRKtTANBgkqhkiG9w0BAQUFADB6MQswCQYDVQQGEwJV
|
|
3
|
+
UzELMAkGA1UECBMCQ0ExITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
|
|
4
|
+
ZDEWMBQGA1UEAxMNUHVmZmluZyBCaWxseTEjMCEGCSqGSIb3DQEJARYUb2xseS5z
|
|
5
|
+
bWl0aEBnbWFpbC5jb20wHhcNMTIxMDA0MDgwNzMxWhcNMTIxMTAzMDgwNzMxWjB6
|
|
6
|
+
MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExITAfBgNVBAoTGEludGVybmV0IFdp
|
|
7
|
+
ZGdpdHMgUHR5IEx0ZDEWMBQGA1UEAxMNUHVmZmluZyBCaWxseTEjMCEGCSqGSIb3
|
|
8
|
+
DQEJARYUb2xseS5zbWl0aEBnbWFpbC5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0A
|
|
9
|
+
MIGJAoGBAKsUa8bzUeTfC05JFwjBXnD+nBlil5aVTDOB719WJkiq1eDY5WWBaMhk
|
|
10
|
+
OqlZkNhl1rg7LmG/0NhBBUdICYTblNRXzhEfaejqOIGQ3FZMR3W/b87cBp4S+8b/
|
|
11
|
+
q9xJPSUgFvQUzX4v+2+//wdiO8qPioinDdRG9RhxwNvS34bqJqpfAgMBAAEwDQYJ
|
|
12
|
+
KoZIhvcNAQEFBQADgYEADnHq4MP+LgTdp4+ycyearUZh4uhAPzxYXTGDirkkFFQ4
|
|
13
|
+
YA2QT0zXgPq1JmrZwV5Wo1wKoIq8LtfeYoDou0VDKtZ6Up13c98R+1yuQE2kgNoG
|
|
14
|
+
9VGn/2la4mvLpj+9Zt8wfNxibFi+ajZ7/zsebI7UM+pW0a3SDowDeU0nVPAmDRM=
|
|
15
|
+
-----END CERTIFICATE-----
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
|
2
|
+
MIICXgIBAAKBgQCrFGvG81Hk3wtOSRcIwV5w/pwZYpeWlUwzge9fViZIqtXg2OVl
|
|
3
|
+
gWjIZDqpWZDYZda4Oy5hv9DYQQVHSAmE25TUV84RH2no6jiBkNxWTEd1v2/O3Aae
|
|
4
|
+
EvvG/6vcST0lIBb0FM1+L/tvv/8HYjvKj4qIpw3URvUYccDb0t+G6iaqXwIDAQAB
|
|
5
|
+
AoGBAJccJ4KIYzqUZGkWmBjsq92Elx64/gpM/wyz5VpBPvmKo/XBvwWkg4gVN9dj
|
|
6
|
+
vFPXyAvcgkBm7DJHZEEs+PN3/IEMyLHWVA+C+C3AHisR/E2yl/AyD9oX8F0KDu+Q
|
|
7
|
+
8bnsL1rFL5zP1saw+QiyofQ13HKtCOj8zjhAUCyZl1NBdiqxAkEA2PNxBHFBSnPU
|
|
8
|
+
L6+PiCvDOJcbqrHQa5kYcS312SNqX/VQ9BtIJfIQ7QA/ueKIUd40OfNBc/jiOC1x
|
|
9
|
+
mlSwlsLnFwJBAMnfWagBvlinIwXw4yhAgsqt012gOzPIHCM7FWipJPbCm9LtJykE
|
|
10
|
+
dGuA06TRE2ZKQq+Oh2yomNni4/LfPVQ6Y/kCQHkej/4W7IiQWem1bcBsDjVNx1ho
|
|
11
|
+
pR8s/YRSUGrFZuHjpyphAMqOdfyaovk4CzsJfsbLk8MXM9SBKmcq2NuSPEkCQQCM
|
|
12
|
+
MvTmTIewtCsLtjdcvijXsA9KR7y2ArUf9qmwrUABrDhiLdfzkad0/dx+68FIWiyk
|
|
13
|
+
Fh2RZin5sKzVARtrwr2pAkEAgIDCCl3YC7Jk5GOm1xVjb999hZSyasOasiVsAafa
|
|
14
|
+
4UzoA4HlDjTWEROObX8ijbsWRU16/yotbWXDa9XXxnDV3A==
|
|
15
|
+
-----END RSA PRIVATE KEY-----
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module AgileProxy
|
|
2
|
+
module Test
|
|
3
|
+
module Integration
|
|
4
|
+
# A helper for 'request spec' integration tests
|
|
5
|
+
module RequestSpecHelper
|
|
6
|
+
def load_small_set_of_request_specs(options = {})
|
|
7
|
+
let(:recordings_resource) { RestClient::Resource.new "http://localhost:3020/api/v1/users/1/applications/#{@recording_application_id}/recordings", headers: { content_type: 'application/json' } }
|
|
8
|
+
before :context do
|
|
9
|
+
|
|
10
|
+
def configure_applications
|
|
11
|
+
application_resource.delete # Delete all applications
|
|
12
|
+
@non_recording_application_id = JSON.parse(application_resource.post user_id: 1, name: 'Non recording app', username: 'anonymous', password: 'password')['id']
|
|
13
|
+
@recording_application_id = JSON.parse(application_resource.post user_id: 1, name: 'Recording app', username: 'recording', password: 'password', record_requests: true)['id']
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def application_resource
|
|
17
|
+
@__application_resource ||= RestClient::Resource.new 'http://localhost:3020/api/v1/users/1/applications', headers: { content_type: 'application/json' }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def create_request_spec(attrs)
|
|
21
|
+
non_recording_resource.post ActiveSupport::JSON.encode attrs
|
|
22
|
+
recording_resource.post ActiveSupport::JSON.encode attrs
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def non_recording_resource
|
|
26
|
+
@__non_recording_resource ||= RestClient::Resource.new "http://localhost:3020/api/v1/users/1/applications/#{@non_recording_application_id}/request_specs", headers: { content_type: 'application/json' }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def recording_resource
|
|
30
|
+
@__recording_resource ||= RestClient::Resource.new "http://localhost:3020/api/v1/users/1/applications/#{@recording_application_id}/request_specs", headers: { content_type: 'application/json' }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Delete all first
|
|
34
|
+
configure_applications
|
|
35
|
+
# Now, add some stubs via the REST interface
|
|
36
|
+
[@http_url, @https_url].each do |url|
|
|
37
|
+
create_request_spec url: "#{url}/index.html", response: { content_type: 'text/html', content: '<html><body>Mocked Content</body></html>' }
|
|
38
|
+
create_request_spec url: "#{url}/api/forums", response: { content_type: 'application/json', content: JSON.pretty_generate(forums: [], total: 0) }
|
|
39
|
+
create_request_spec url: "#{url}/api/forums", http_method: 'POST', response: { content_type: 'application/json', content: '{"created": true}' }
|
|
40
|
+
create_request_spec url: "#{url}/api/forums/:forum_id/posts", response: { content_type: 'application/json', content: JSON.pretty_generate(posts: [
|
|
41
|
+
{ forum_id: '{{forum_id}}', subject: 'My first post' },
|
|
42
|
+
{ forum_id: '{{forum_id}}', subject: 'My second post' },
|
|
43
|
+
{ forum_id: '{{forum_id}}', subject: 'My third post' }
|
|
44
|
+
], total: 3), is_template: true }
|
|
45
|
+
create_request_spec url: "#{url}/api/forums/:forum_id/:post_id", response: { content_type: 'text/html', content: '<html><body><h1>Sorted By: {{sort}}</h1><h2>{{forum_id}}</h2><h3>{{post_id}}</h3></body></html>', is_template: true }
|
|
46
|
+
create_request_spec url: "#{url}/api/forums/:forum_id/:post_id", http_method: 'PUT', response: { content_type: 'application/json', content: '{"updated": true}' }
|
|
47
|
+
create_request_spec url: "#{url}/api/forums/:forum_id/:post_id", http_method: 'DELETE', response: { content_type: 'application/json', content: '{"deleted": true}' }
|
|
48
|
+
create_request_spec url: "#{url}/api/forums/:forum_id/:post_id", conditions: '{"post_id": "special"}', response: { content_type: 'text/html', content: '<html><body><h1>Sorted By: {{sort}}</h1><h2>{{forum_id}}</h2><h3>{{post_id}}</h3><p>This is a special response</p></body></html>', is_template: true }
|
|
49
|
+
create_request_spec url: "#{url}/api/forums/:forum_id/:post_id", conditions: '{"post_id": "special", "sort": "eversospecial"}', response: { content_type: 'text/html', content: '<html><body><h1>Sorted By: {{sort}}</h1><h2>{{forum_id}}</h2><h3>{{post_id}}</h3><p>This is an ever so special response</p></body></html>', is_template: true }
|
|
50
|
+
create_request_spec url: "#{url}/api/forums/:forum_id", http_method: 'POST', conditions: '{"posted_var":"special_value"}', response: { content_type: 'text/html', content: '<html><body><h1></h1><h2>{{posted_var}}</h2><h3>{{forum_id}}</h3><p>This should get data from the POSTed data</p></body></html>', is_template: true }
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
before :each do
|
|
54
|
+
recordings_resource.delete if options.key?(:recording) && options[:recording]
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
require 'integration_spec_helper'
|
|
2
|
+
|
|
3
|
+
# require 'spec_helper'
|
|
4
|
+
require 'agile_proxy'
|
|
5
|
+
require 'resolv'
|
|
6
|
+
|
|
7
|
+
shared_examples_for 'a proxy server' do |options = {}|
|
|
8
|
+
if options.key?(:recording) && options[:recording]
|
|
9
|
+
after :each do
|
|
10
|
+
expect(JSON.parse(recordings_resource.get)['total']).to eql(1)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
it 'should proxy GET requests' do
|
|
14
|
+
expect(http.get('/echo').body).to eql 'GET /echo'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'should proxy POST requests' do
|
|
18
|
+
expect(http.post('/echo', foo: 'bar').body).to eql "POST /echo\nfoo=bar"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'should proxy PUT requests' do
|
|
22
|
+
expect(http.post('/echo', foo: 'bar').body).to eql "POST /echo\nfoo=bar"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'should proxy HEAD requests' do
|
|
26
|
+
expect(http.head('/echo').headers['HTTP-X-EchoServer']).to eql 'HEAD /echo'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'should proxy DELETE requests' do
|
|
30
|
+
expect(http.delete('/echo').body).to eql 'DELETE /echo'
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
shared_examples_for 'a request stub' do |options = {}|
|
|
35
|
+
if options.key?(:recording) && options[:recording]
|
|
36
|
+
after :each do
|
|
37
|
+
data = recordings_resource.get
|
|
38
|
+
count = JSON.parse(data)['total']
|
|
39
|
+
expect(count).to eql(1)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
it 'should stub GET requests' do
|
|
43
|
+
expect(http.get('/index.html').body).to eql '<html><body>Mocked Content</body></html>'
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'should stub GET response statuses' do
|
|
47
|
+
expect(http.get('/index.html').status).to eql 200
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'Should stub a different get request with json response' do
|
|
51
|
+
resp = http.get('/api/forums')
|
|
52
|
+
expect(ActiveSupport::JSON.decode(resp.body).symbolize_keys).to eql forums: [], total: 0
|
|
53
|
+
expect(resp.status).to eql 200
|
|
54
|
+
expect(resp.headers['Content-Type']).to eql 'application/json'
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'Should get the mocked content with parameter substitution for the /api/forums/:forum_id/posts url' do
|
|
58
|
+
resp = http.get '/api/forums/my_forum/posts'
|
|
59
|
+
expect(ActiveSupport::JSON.decode(resp.body)).to eql 'posts' => [{ 'forum_id' => 'my_forum', 'subject' => 'My first post' }, { 'forum_id' => 'my_forum', 'subject' => 'My second post' }, { 'forum_id' => 'my_forum', 'subject' => 'My third post' }], 'total' => 3
|
|
60
|
+
expect(resp.status).to eql 200
|
|
61
|
+
expect(resp.headers['Content-Type']).to eql 'application/json'
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'Should get the mocked content for api/forums/:forum_id/:post_id with parameter substitution including query parameters' do
|
|
65
|
+
resp = http.get '/api/forums/my_forum/my_post?sort=id'
|
|
66
|
+
expect(resp.body).to eql '<html><body><h1>Sorted By: id</h1><h2>my_forum</h2><h3>my_post</h3></body></html>'
|
|
67
|
+
expect(resp.status).to eql 200
|
|
68
|
+
expect(resp.headers['Content-Type']).to eql 'text/html'
|
|
69
|
+
end
|
|
70
|
+
it 'Should get the mocked content for api/forums/:forum_id/:post_id.html with parameter substitution including query parameters' do
|
|
71
|
+
resp = http.get '/api/forums/my_forum/my_post.html?sort=id'
|
|
72
|
+
expect(resp.body).to eql '<html><body><h1>Sorted By: id</h1><h2>my_forum</h2><h3>my_post</h3></body></html>'
|
|
73
|
+
expect(resp.status).to eql 200
|
|
74
|
+
expect(resp.headers['Content-Type']).to eql 'text/html'
|
|
75
|
+
end
|
|
76
|
+
it 'Should respond with an error for api/forums/:forum_id/:post_id.html with parameter substitution with a missing query parameter' do
|
|
77
|
+
resp = http.get '/api/forums/my_forum/my_post.html'
|
|
78
|
+
expect(resp.body).to eql "Missing var or method 'sort' in data."
|
|
79
|
+
expect(resp.status).to eql 500
|
|
80
|
+
end
|
|
81
|
+
it 'Should match the route by posted json data and the posted data can be output via the template' do
|
|
82
|
+
resp = http.post '/api/forums/my_forum', '{"posted_var": "special_value"}', 'Content-Type' => 'application/json'
|
|
83
|
+
expect(resp.body).to eql '<html><body><h1></h1><h2>special_value</h2><h3>my_forum</h3><p>This should get data from the POSTed data</p></body></html>'
|
|
84
|
+
expect(resp.status).to eql 200
|
|
85
|
+
expect(resp.headers['Content-Type']).to eql 'text/html'
|
|
86
|
+
end
|
|
87
|
+
# it 'Should match the route by posted xml data and the posted data can be output via the template' do
|
|
88
|
+
# resp = http.post "/api/forums/my_forum", '<posted_var>special_value</posted_var>', {'Content-Type' => 'application/xml'}
|
|
89
|
+
# expect(resp.body).to eql '<html><body><h1></h1><h2>special_value</h2><h3>my_forum</h3><p>This should get data from the POSTed data</p></body></html>'
|
|
90
|
+
# expect(resp.status).to eql 200
|
|
91
|
+
# end
|
|
92
|
+
it 'Should match the route by posted url encoded data and the posted data can be output via the template' do
|
|
93
|
+
resp = http.post '/api/forums/my_forum', 'posted_var=special_value', 'Content-Type' => 'application/x-www-form-urlencoded'
|
|
94
|
+
expect(resp.body).to eql '<html><body><h1></h1><h2>special_value</h2><h3>my_forum</h3><p>This should get data from the POSTed data</p></body></html>'
|
|
95
|
+
expect(resp.status).to eql 200
|
|
96
|
+
end
|
|
97
|
+
it 'Should match the route by posted multipart encoded data and the posted data can be output via the template' do
|
|
98
|
+
resp = http.post '/api/forums/my_forum', 'posted_var=special_value', 'Content-Type' => 'multipart/form-data'
|
|
99
|
+
expect(resp.body).to eql '<html><body><h1></h1><h2>special_value</h2><h3>my_forum</h3><p>This should get data from the POSTed data</p></body></html>'
|
|
100
|
+
expect(resp.status).to eql 200
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it 'should stub POST requests' do
|
|
104
|
+
resp = http.post('/api/forums', foo: :bar)
|
|
105
|
+
expect(resp.body).to eql '{"created": true}'
|
|
106
|
+
expect(resp.headers['Content-Type']).to eql 'application/json'
|
|
107
|
+
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it 'should stub PUT requests' do
|
|
111
|
+
resp = http.put('/api/forums/forum_1/my_post', foo: :bar)
|
|
112
|
+
expect(resp.body).to eql '{"updated": true}'
|
|
113
|
+
expect(resp.headers['Content-Type']).to eql 'application/json'
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it 'should stub DELETE requests' do
|
|
117
|
+
resp = http.delete('/api/forums/forum_1/my_post')
|
|
118
|
+
expect(resp.body).to eql '{"deleted": true}'
|
|
119
|
+
expect(resp.headers['Content-Type']).to eql 'application/json'
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
shared_examples_for 'a cache' do
|
|
124
|
+
|
|
125
|
+
context 'whitelisted GET requests' do
|
|
126
|
+
it 'should not be cached' do
|
|
127
|
+
assert_noncached_url
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
context 'with ports' do
|
|
131
|
+
before do
|
|
132
|
+
rack_app_url = URI(http.url_prefix)
|
|
133
|
+
AgileProxy.config.whitelist = ["#{rack_app_url.host}:#{rack_app_url.port}"]
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it 'should not be cached ' do
|
|
137
|
+
assert_noncached_url
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
context 'non-whitelisted GET requests' do
|
|
143
|
+
before do
|
|
144
|
+
AgileProxy.config.whitelist = []
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
it 'should be cached' do
|
|
148
|
+
assert_cached_url
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
context 'with ports' do
|
|
152
|
+
before do
|
|
153
|
+
rack_app_url = URI(http.url_prefix)
|
|
154
|
+
AgileProxy.config.whitelist = ["#{rack_app_url.host}:#{rack_app_url.port + 1}"]
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it 'should be cached' do
|
|
158
|
+
assert_cached_url
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
context 'ignore_params GET requests' do
|
|
164
|
+
before do
|
|
165
|
+
AgileProxy.config.ignore_params = ['/analytics']
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it 'should be cached' do
|
|
169
|
+
r = http.get('/analytics?some_param=5')
|
|
170
|
+
expect(r.body).to eql 'GET /analytics'
|
|
171
|
+
expect do
|
|
172
|
+
expect do
|
|
173
|
+
r = http.get('/analytics?some_param=20')
|
|
174
|
+
end.to change { r.headers['HTTP-X-EchoCount'].to_i }.by(1)
|
|
175
|
+
end.to_not change { r.body }
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
context 'path_blacklist GET requests' do
|
|
180
|
+
before do
|
|
181
|
+
AgileProxy.config.path_blacklist = ['/api']
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it 'should be cached' do
|
|
185
|
+
assert_cached_url('/api')
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
context 'cache persistence' do
|
|
190
|
+
let(:cached_key) { proxy.cache.key('get', "#{url}/foo", '') }
|
|
191
|
+
let(:cached_file) do
|
|
192
|
+
f = cached_key + '.yml'
|
|
193
|
+
File.join(AgileProxy.config.cache_path, f)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
before { AgileProxy.config.whitelist = [] }
|
|
197
|
+
|
|
198
|
+
after do
|
|
199
|
+
File.delete(cached_file) if File.exist?(cached_file)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
context 'enabled' do
|
|
203
|
+
before { AgileProxy.config.persist_cache = true }
|
|
204
|
+
|
|
205
|
+
it 'should persist' do
|
|
206
|
+
http.get('/foo')
|
|
207
|
+
expect(File.exist?(cached_file)).to be_true
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
it 'should be read initially from persistent cache' do
|
|
211
|
+
File.open(cached_file, 'w') do |f|
|
|
212
|
+
cached = {
|
|
213
|
+
headers: {},
|
|
214
|
+
content: 'GET /foo cached'
|
|
215
|
+
}
|
|
216
|
+
f.write(cached.to_yaml(Encoding: :Utf8))
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
r = http.get('/foo')
|
|
220
|
+
expect(r.body).to eql 'GET /foo cached'
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
context 'cache_request_headers requests' do
|
|
224
|
+
it 'should not be cached by default' do
|
|
225
|
+
http.get('/foo')
|
|
226
|
+
saved_cache = AgileProxy.proxy.cache.fetch_from_persistence(cached_key)
|
|
227
|
+
expect(saved_cache.keys).not_to include :request_headers
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
context 'when enabled' do
|
|
231
|
+
before do
|
|
232
|
+
AgileProxy.config.cache_request_headers = true
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it 'should be cached' do
|
|
236
|
+
http.get('/foo')
|
|
237
|
+
saved_cache = AgileProxy.proxy.cache.fetch_from_persistence(cached_key)
|
|
238
|
+
expect(saved_cache.keys).to include :request_headers
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
context 'ignore_cache_port requests' do
|
|
244
|
+
it 'should be cached without port' do
|
|
245
|
+
r = http.get('/foo')
|
|
246
|
+
url = URI(r.env[:url])
|
|
247
|
+
saved_cache = AgileProxy.proxy.cache.fetch_from_persistence(cached_key)
|
|
248
|
+
|
|
249
|
+
expect(saved_cache[:url]).to_not eql(url.to_s)
|
|
250
|
+
expect(saved_cache[:url]).to eql(url.to_s.gsub(":#{url.port}", ''))
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
context 'non_whitelisted_requests_disabled requests' do
|
|
255
|
+
before { AgileProxy.config.non_whitelisted_requests_disabled = true }
|
|
256
|
+
|
|
257
|
+
it 'should raise error when disabled' do
|
|
258
|
+
# TODO: Suppress stderr output: https://gist.github.com/adamstegman/926858
|
|
259
|
+
expect { http.get('/foo') }.to raise_error(Faraday::Error::ConnectionFailed, 'end of file reached')
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
context 'non_successful_cache_disabled requests' do
|
|
264
|
+
before do
|
|
265
|
+
rack_app_url = URI(http_error.url_prefix)
|
|
266
|
+
AgileProxy.config.whitelist = ["#{rack_app_url.host}:#{rack_app_url.port}"]
|
|
267
|
+
AgileProxy.config.non_successful_cache_disabled = true
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
it 'should not cache non-successful response when enabled' do
|
|
271
|
+
http_error.get('/foo')
|
|
272
|
+
expect(File.exist?(cached_file)).to be_false
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
it 'should cache successful response when enabled' do
|
|
276
|
+
assert_cached_url
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
context 'non_successful_error_level requests' do
|
|
281
|
+
before do
|
|
282
|
+
rack_app_url = URI(http_error.url_prefix)
|
|
283
|
+
AgileProxy.config.whitelist = ["#{rack_app_url.host}:#{rack_app_url.port}"]
|
|
284
|
+
AgileProxy.config.non_successful_error_level = :error
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
it 'should raise error for non-successful responses when :error' do
|
|
288
|
+
# When this config setting is set, the EventMachine running the test servers is killed upon error raising
|
|
289
|
+
# The `raise` is required to bubble up the error to the test running it
|
|
290
|
+
# The Faraday error is raised upon `close_connection` so this can be non-pending if we can do one of the following:
|
|
291
|
+
# 1) Remove the `raise error_message` conditionally for this test
|
|
292
|
+
# 2) Restart the test servers if they aren't running
|
|
293
|
+
# 3) Change the test servers to start/stop for each test instead of before all
|
|
294
|
+
# 4) Remove the test server completely and rely on the server instantiated by the app
|
|
295
|
+
pending 'Unable to test this without affecting the running test servers'
|
|
296
|
+
expect { http_error.get('/foo') }.to raise_error(Faraday::Error::ConnectionFailed)
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
context 'disabled' do
|
|
301
|
+
before { AgileProxy.config.persist_cache = false }
|
|
302
|
+
|
|
303
|
+
it 'shouldnt persist' do
|
|
304
|
+
http.get('/foo')
|
|
305
|
+
expect(File.exist?(cached_file)).to be_false
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def assert_noncached_url(url = '/foo')
|
|
311
|
+
r = http.get(url)
|
|
312
|
+
expect(r.body).to eql "GET #{url}"
|
|
313
|
+
expect do
|
|
314
|
+
expect do
|
|
315
|
+
r = http.get(url)
|
|
316
|
+
end.to change { r.headers['HTTP-X-EchoCount'].to_i }.by(1)
|
|
317
|
+
end.to_not change { r.body }
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def assert_cached_url(url = '/foo')
|
|
321
|
+
r = http.get(url)
|
|
322
|
+
expect(r.body).to eql "GET #{url}"
|
|
323
|
+
expect do
|
|
324
|
+
expect do
|
|
325
|
+
r = http.get(url)
|
|
326
|
+
end.to_not change { r.headers['HTTP-X-EchoCount'] }
|
|
327
|
+
end.to_not change { r.body }
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
describe AgileProxy::Server do
|
|
331
|
+
extend AgileProxy::Test::Integration::RequestSpecHelper
|
|
332
|
+
describe 'Without recording' do
|
|
333
|
+
load_small_set_of_request_specs
|
|
334
|
+
before do
|
|
335
|
+
# Adding non-valid Faraday options throw an error: https://github.com/arsduo/koala/pull/311
|
|
336
|
+
# Valid options: :request, :proxy, :ssl, :builder, :url, :parallel_manager, :params, :headers, :builder_class
|
|
337
|
+
faraday_options = {
|
|
338
|
+
proxy: { uri: 'http://anonymous:password@localhost:3100' },
|
|
339
|
+
request: { timeout: 10.0 }
|
|
340
|
+
}
|
|
341
|
+
@http = Faraday.new @http_url, faraday_options
|
|
342
|
+
@https = Faraday.new @https_url, faraday_options.merge(ssl: { verify: false })
|
|
343
|
+
@http_error = Faraday.new @error_url, faraday_options
|
|
344
|
+
end
|
|
345
|
+
context 'proxying' do
|
|
346
|
+
context 'HTTP' do
|
|
347
|
+
let!(:http) { @http }
|
|
348
|
+
it_should_behave_like 'a proxy server'
|
|
349
|
+
end
|
|
350
|
+
context 'HTTPS' do
|
|
351
|
+
let!(:http) { @https }
|
|
352
|
+
it_should_behave_like 'a proxy server'
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
context 'stubbing' do
|
|
356
|
+
context 'HTTP' do
|
|
357
|
+
let!(:url) { @http_url }
|
|
358
|
+
let!(:http) { @http }
|
|
359
|
+
it_should_behave_like 'a request stub'
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
context 'HTTPS' do
|
|
363
|
+
let!(:url) { @https_url }
|
|
364
|
+
let!(:http) { @https }
|
|
365
|
+
it_should_behave_like 'a request stub'
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
describe 'With recording' do
|
|
371
|
+
load_small_set_of_request_specs recording: true
|
|
372
|
+
before do
|
|
373
|
+
# Adding non-valid Faraday options throw an error: https://github.com/arsduo/koala/pull/311
|
|
374
|
+
# Valid options: :request, :proxy, :ssl, :builder, :url, :parallel_manager, :params, :headers, :builder_class
|
|
375
|
+
faraday_options = {
|
|
376
|
+
proxy: { uri: 'http://recording:password@localhost:3100' },
|
|
377
|
+
request: { timeout: 10.0 }
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
@http = Faraday.new @http_url, faraday_options
|
|
381
|
+
@https = Faraday.new @https_url, faraday_options.merge(ssl: { verify: false })
|
|
382
|
+
@http_error = Faraday.new @error_url, faraday_options
|
|
383
|
+
end
|
|
384
|
+
context 'proxying' do
|
|
385
|
+
context 'HTTP' do
|
|
386
|
+
let!(:http) { @http }
|
|
387
|
+
it_should_behave_like 'a proxy server', recording: true
|
|
388
|
+
end
|
|
389
|
+
context 'HTTPS' do
|
|
390
|
+
let!(:http) { @https }
|
|
391
|
+
it_should_behave_like 'a proxy server', recording: true
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
context 'stubbing' do
|
|
395
|
+
context 'HTTP' do
|
|
396
|
+
let!(:url) { @http_url }
|
|
397
|
+
let!(:http) { @http }
|
|
398
|
+
it_should_behave_like 'a request stub', recording: true
|
|
399
|
+
end
|
|
400
|
+
context 'HTTPS' do
|
|
401
|
+
let!(:url) { @https_url }
|
|
402
|
+
let!(:http) { @https }
|
|
403
|
+
it_should_behave_like 'a request stub', recording: true
|
|
404
|
+
end
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
end
|