puffing-billy 0.2.1 → 0.2.3
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/.gitignore +1 -0
- data/Gemfile.lock +78 -66
- data/README.md +111 -6
- data/lib/billy.rb +6 -4
- data/lib/billy/cache.rb +71 -38
- data/lib/billy/config.rb +10 -2
- data/lib/billy/json_utils.rb +40 -0
- data/lib/billy/proxy.rb +4 -2
- data/lib/billy/proxy_connection.rb +85 -19
- data/lib/billy/proxy_request_stub.rb +1 -1
- data/lib/billy/railtie.rb +9 -0
- data/lib/billy/rspec.rb +0 -1
- data/lib/billy/version.rb +1 -1
- data/lib/puffing-billy.rb +2 -0
- data/lib/puffing-billy/rspec.rb +2 -0
- data/lib/tasks/billy.rake +87 -0
- data/puffing-billy.gemspec +2 -2
- data/spec/features/examples/facebook_api_spec.rb +2 -1
- data/spec/features/examples/tumblr_api_spec.rb +4 -4
- data/spec/lib/billy/cache_spec.rb +37 -0
- data/spec/lib/billy/proxy_request_stub_spec.rb +42 -32
- data/spec/lib/billy/resource_utils_spec.rb +55 -0
- data/spec/lib/proxy_spec.rb +233 -45
- data/spec/support/test_server.rb +44 -28
- metadata +55 -82
data/lib/billy/config.rb
CHANGED
@@ -5,7 +5,9 @@ module Billy
|
|
5
5
|
class Config
|
6
6
|
DEFAULT_WHITELIST = ['127.0.0.1', 'localhost']
|
7
7
|
|
8
|
-
attr_accessor :logger, :cache, :
|
8
|
+
attr_accessor :logger, :cache, :cache_request_headers, :whitelist, :path_blacklist, :ignore_params,
|
9
|
+
:persist_cache, :ignore_cache_port, :non_successful_cache_disabled, :non_successful_error_level,
|
10
|
+
:non_whitelisted_requests_disabled, :cache_path
|
9
11
|
|
10
12
|
def initialize
|
11
13
|
@logger = defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
|
@@ -14,10 +16,16 @@ module Billy
|
|
14
16
|
|
15
17
|
def reset
|
16
18
|
@cache = true
|
19
|
+
@cache_request_headers = false
|
17
20
|
@whitelist = DEFAULT_WHITELIST
|
21
|
+
@path_blacklist = []
|
18
22
|
@ignore_params = []
|
19
23
|
@persist_cache = false
|
20
|
-
@
|
24
|
+
@ignore_cache_port = true
|
25
|
+
@non_successful_cache_disabled = false
|
26
|
+
@non_successful_error_level = :warn
|
27
|
+
@non_whitelisted_requests_disabled = false
|
28
|
+
@cache_path = File.join(Dir.tmpdir, 'puffing-billy')
|
21
29
|
end
|
22
30
|
end
|
23
31
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Billy
|
5
|
+
module JSONUtils
|
6
|
+
|
7
|
+
def self.json?(value)
|
8
|
+
!!JSON.parse(value)
|
9
|
+
rescue JSON::ParserError, TypeError
|
10
|
+
false
|
11
|
+
end
|
12
|
+
|
13
|
+
# Recursively sorts the key/value pairs of all hashes within the given
|
14
|
+
# data structure while preserving the order of arrays.
|
15
|
+
def self.sort_hash_keys(data)
|
16
|
+
return data unless data.is_a?(Hash) || data.is_a?(Array)
|
17
|
+
if data.is_a? Hash
|
18
|
+
data.keys.sort.reduce({}) do |seed, key|
|
19
|
+
seed[key] = sort_hash_keys(data[key])
|
20
|
+
seed
|
21
|
+
end
|
22
|
+
else
|
23
|
+
data.map do |element|
|
24
|
+
sort_hash_keys(element)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Recursively sorts the name/value pairs of JSON objects. For instance,
|
30
|
+
# sort_json('{"b" : "2", "a" : "1"}') == sort_json('{"a" : "1", "b" : "2"}')
|
31
|
+
# Per http://json.org/, "An object is an unordered set of name/value pairs"
|
32
|
+
# and "An array is an ordered collection of values". So, we can sort the
|
33
|
+
# name/value pairs by name (key), but we must preserve the order of an array.
|
34
|
+
# Processing JSON in this way enables a consistent SHA to be derived from
|
35
|
+
# JSON payloads which have the same name/value pairs, but different orders.
|
36
|
+
def self.sort_json(json_str)
|
37
|
+
JSONUtils::sort_hash_keys(JSON.parse(json_str, symbolize_names: true)).to_json
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/billy/proxy.rb
CHANGED
@@ -4,6 +4,8 @@ require 'eventmachine'
|
|
4
4
|
|
5
5
|
module Billy
|
6
6
|
class Proxy
|
7
|
+
attr_reader :cache
|
8
|
+
|
7
9
|
def initialize
|
8
10
|
reset
|
9
11
|
@cache = Billy::Cache.new
|
@@ -54,8 +56,8 @@ module Billy
|
|
54
56
|
end
|
55
57
|
|
56
58
|
def restore_cache
|
59
|
+
warn "[DEPRECATION] `restore_cache` is deprecated as cache files are dynamincally checked. Use `reset_cache` if you just want to clear the cache."
|
57
60
|
@cache.reset
|
58
|
-
@cache.load_dir
|
59
61
|
end
|
60
62
|
|
61
63
|
protected
|
@@ -76,7 +78,7 @@ module Billy
|
|
76
78
|
p.cache = @cache
|
77
79
|
end
|
78
80
|
|
79
|
-
Billy.log(:info, "Proxy listening on #{url}")
|
81
|
+
Billy.log(:info, "puffing-billy: Proxy listening on #{url}")
|
80
82
|
end
|
81
83
|
end
|
82
84
|
end
|
@@ -74,21 +74,38 @@ module Billy
|
|
74
74
|
end
|
75
75
|
|
76
76
|
if result
|
77
|
-
Billy.log(:info, "STUB #{@parser.http_method} #{@url}")
|
78
|
-
|
79
|
-
response.status = result[0]
|
80
|
-
response.headers = result[1].merge('Connection' => 'close')
|
81
|
-
response.content = result[2]
|
82
|
-
response.send_response
|
77
|
+
Billy.log(:info, "puffing-billy: STUB #{@parser.http_method} for '#{@url}'")
|
78
|
+
stub_request(result)
|
83
79
|
elsif cache.cached?(@parser.http_method.downcase, @url, @body)
|
84
|
-
Billy.log(:info, "CACHE #{@parser.http_method} #{@url}")
|
80
|
+
Billy.log(:info, "puffing-billy: CACHE #{@parser.http_method} for '#{@url}'")
|
85
81
|
respond_from_cache
|
86
|
-
|
87
|
-
Billy.log(:info, "PROXY #{@parser.http_method} #{@url}")
|
82
|
+
elsif !disabled_request?
|
83
|
+
Billy.log(:info, "puffing-billy: PROXY #{@parser.http_method} for '#{@url}'")
|
88
84
|
proxy_request
|
85
|
+
else
|
86
|
+
close_connection
|
87
|
+
body_msg = @parser.http_method == 'post' ? " with body '#{@body}'" : ''
|
88
|
+
raise "puffing-billy: Connection to #{@url}#{body_msg} not cached and new http connections are disabled"
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
+
def stub_request(result)
|
93
|
+
response = EM::DelegatedHttpResponse.new(self)
|
94
|
+
response.status = result[0]
|
95
|
+
response.headers = result[1].merge('Connection' => 'close')
|
96
|
+
response.content = result[2]
|
97
|
+
response.send_response
|
98
|
+
end
|
99
|
+
|
100
|
+
def respond_from_cache
|
101
|
+
cached_res = cache.fetch(@parser.http_method.downcase, @url, @body)
|
102
|
+
res = EM::DelegatedHttpResponse.new(self)
|
103
|
+
res.status = cached_res[:status]
|
104
|
+
res.headers = cached_res[:headers]
|
105
|
+
res.content = cached_res[:content]
|
106
|
+
res.send_response
|
107
|
+
end
|
108
|
+
|
92
109
|
def proxy_request
|
93
110
|
headers = Hash[@headers.map { |k,v| [k.downcase, v] }]
|
94
111
|
headers.delete('accept-encoding')
|
@@ -105,7 +122,7 @@ module Billy
|
|
105
122
|
req = req.send(@parser.http_method.downcase, req_opts)
|
106
123
|
|
107
124
|
req.errback do
|
108
|
-
Billy.log(:error, "Request failed: #{@url}")
|
125
|
+
Billy.log(:error, "puffing-billy: Request failed: #{@url}")
|
109
126
|
close_connection
|
110
127
|
end
|
111
128
|
|
@@ -115,8 +132,11 @@ module Billy
|
|
115
132
|
res_headers = res_headers.merge('Connection' => 'close')
|
116
133
|
res_headers.delete('Transfer-Encoding')
|
117
134
|
res_content = req.response.force_encoding('BINARY')
|
118
|
-
|
119
|
-
|
135
|
+
|
136
|
+
handle_response_code(res_status)
|
137
|
+
|
138
|
+
if cacheable?(res_headers, res_status)
|
139
|
+
cache.store(@parser.http_method.downcase, @url, headers, @body, res_headers, res_status, res_content)
|
120
140
|
end
|
121
141
|
|
122
142
|
res = EM::DelegatedHttpResponse.new(self)
|
@@ -127,13 +147,59 @@ module Billy
|
|
127
147
|
end
|
128
148
|
end
|
129
149
|
|
130
|
-
def
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
150
|
+
def disabled_request?
|
151
|
+
url = URI(@url)
|
152
|
+
# In isolated environments, you may want to stop the request from happening
|
153
|
+
# or else you get "getaddrinfo: Name or service not known" errors
|
154
|
+
if Billy.config.non_whitelisted_requests_disabled
|
155
|
+
blacklisted_path?(url.path) || !whitelisted_url?(url)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def handle_response_code(status)
|
160
|
+
log_level = successful_status?(status) ? :info : Billy.config.non_successful_error_level
|
161
|
+
log_message = "puffing-billy: Received response status code #{status} for #{@url}"
|
162
|
+
Billy.log(log_level, log_message)
|
163
|
+
if log_level == :error
|
164
|
+
close_connection
|
165
|
+
raise log_message
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def cacheable?(headers, status)
|
170
|
+
if Billy.config.cache
|
171
|
+
url = URI(@url)
|
172
|
+
# Cache the responses if they aren't whitelisted host[:port]s but always cache blacklisted paths on any hosts
|
173
|
+
cacheable_headers?(headers) && cacheable_status?(status) && (!whitelisted_url?(url) || blacklisted_path?(url.path))
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def whitelisted_host?(host)
|
180
|
+
Billy.config.whitelist.include?(host)
|
137
181
|
end
|
182
|
+
|
183
|
+
def whitelisted_url?(url)
|
184
|
+
whitelisted_host?(url.host) || whitelisted_host?("#{url.host}:#{url.port}")
|
185
|
+
end
|
186
|
+
|
187
|
+
def blacklisted_path?(path)
|
188
|
+
Billy.config.path_blacklist.index{|bl| path.include?(bl)}
|
189
|
+
end
|
190
|
+
|
191
|
+
def successful_status?(status)
|
192
|
+
(200..299).include?(status) || status == 304
|
193
|
+
end
|
194
|
+
|
195
|
+
def cacheable_headers?(headers)
|
196
|
+
#TODO: test headers for cacheability (ie. Cache-Control: no-cache)
|
197
|
+
true
|
198
|
+
end
|
199
|
+
|
200
|
+
def cacheable_status?(status)
|
201
|
+
Billy.config.non_successful_cache_disabled ? successful_status?(status) : true
|
202
|
+
end
|
203
|
+
|
138
204
|
end
|
139
205
|
end
|
data/lib/billy/rspec.rb
CHANGED
data/lib/billy/version.rb
CHANGED
@@ -0,0 +1,87 @@
|
|
1
|
+
namespace :cache do
|
2
|
+
|
3
|
+
desc 'Print out all cache file information'
|
4
|
+
task :print_all do
|
5
|
+
cache_array = load_cache
|
6
|
+
|
7
|
+
sort_cache(cache_array).each do |cache|
|
8
|
+
print_cache_details(cache)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
desc 'Print out specific cache file information'
|
13
|
+
task :print_details, :sha do |t, args|
|
14
|
+
raise "Missing sha; usage: rake cache:print_details['<sha>']" unless args[:sha]
|
15
|
+
cache_array = load_cache(Billy.config.cache_path, '*'+args[:sha]+'*.yml')
|
16
|
+
|
17
|
+
sort_cache(cache_array).each do |cache|
|
18
|
+
print_cache_details(cache)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'Find specific cache files by URL'
|
23
|
+
task :find_by_url, :api_path do |t, args|
|
24
|
+
raise "Missing api path; usage: rake cache:find_by_url['<api_path>']" unless args[:api_path]
|
25
|
+
cache_array = load_cache
|
26
|
+
filtered_cache_array = cache_array.select {|f| f[:url_path].include?(args[:api_path]) }
|
27
|
+
|
28
|
+
sort_cache(filtered_cache_array).each do |cache|
|
29
|
+
print_cache_details(cache)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
desc 'Find specific cache files by scope'
|
34
|
+
task :find_by_scope, :scope do |t, args|
|
35
|
+
raise "Missing scope; usage: rake cache:find_by_scope['<scope>']" unless args[:scope]
|
36
|
+
cache_array = load_cache
|
37
|
+
filtered_cache_array = cache_array.select {|f| f[:scope] && f[:scope].include?(args[:scope]) }
|
38
|
+
|
39
|
+
sort_cache(filtered_cache_array).each do |cache|
|
40
|
+
print_cache_details(cache)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
desc 'Find cache files with non-successful status codes'
|
45
|
+
task :find_non_successful do
|
46
|
+
cache_array = load_cache
|
47
|
+
filtered_cache_array = cache_array.select {|f| !(200..299).include?(f[:status]) }
|
48
|
+
|
49
|
+
sort_cache(filtered_cache_array).each do |cache|
|
50
|
+
print_cache_details(cache)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def load_cache(cache_directory = Billy.config.cache_path, file_pattern = '*.yml')
|
55
|
+
cache_path = Rails.root.join(cache_directory)
|
56
|
+
cache_array = []
|
57
|
+
|
58
|
+
Dir.glob(cache_path+file_pattern) do |filename|
|
59
|
+
data = load_cache_file(filename)
|
60
|
+
url = URI(data[:url])
|
61
|
+
data[:url_path] = "#{url.path}#{url.query ? '?'+url.query : ''}#{url.fragment ? '#'+url.fragment : ''}"
|
62
|
+
data[:filename] = filename.gsub(Rails.root.to_s+'/','')
|
63
|
+
cache_array << data
|
64
|
+
end
|
65
|
+
cache_array
|
66
|
+
end
|
67
|
+
|
68
|
+
def load_cache_file(filename)
|
69
|
+
YAML.load(File.open(filename))
|
70
|
+
rescue ArgumentError => e
|
71
|
+
puts "Could not parse YAML: #{e.message}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def print_cache_details(cache)
|
75
|
+
puts " Scope: #{cache[:scope]}" if cache[:scope]
|
76
|
+
puts " URL: #{cache[:url]}"
|
77
|
+
puts " Body: #{cache[:body]}" if cache[:method] == 'post'
|
78
|
+
puts " Details: Request method '#{cache[:method]}' returned response status code: '#{cache[:status]}'"
|
79
|
+
puts "Filename: #{cache[:filename]}"
|
80
|
+
puts "\n\n"
|
81
|
+
end
|
82
|
+
|
83
|
+
def sort_cache(cache, key = :url_path)
|
84
|
+
cache.sort_by { |hsh| hsh[key] }
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
data/puffing-billy.gemspec
CHANGED
@@ -20,11 +20,11 @@ Gem::Specification.new do |gem|
|
|
20
20
|
gem.add_development_dependency "faraday"
|
21
21
|
gem.add_development_dependency "poltergeist"
|
22
22
|
gem.add_development_dependency "selenium-webdriver"
|
23
|
-
gem.add_development_dependency "capybara-webkit"
|
23
|
+
gem.add_development_dependency "capybara-webkit", "~> 1.0"
|
24
24
|
gem.add_development_dependency "rack"
|
25
25
|
gem.add_development_dependency "guard"
|
26
26
|
gem.add_development_dependency "rb-inotify"
|
27
|
-
gem.add_development_dependency "
|
27
|
+
gem.add_development_dependency "pry"
|
28
28
|
gem.add_development_dependency "cucumber"
|
29
29
|
gem.add_runtime_dependency "eventmachine"
|
30
30
|
gem.add_runtime_dependency "em-http-request"
|
@@ -17,6 +17,7 @@ describe 'Facebook API example', :type => :feature, :js => true do
|
|
17
17
|
|
18
18
|
it 'should show me as logged-in', :js => true do
|
19
19
|
visit '/facebook_api.html'
|
20
|
-
|
20
|
+
click_on "Login"
|
21
|
+
expect(page).to have_content "Hi, Tester 1"
|
21
22
|
end
|
22
23
|
end
|
@@ -21,9 +21,9 @@ describe 'Tumblr API example', :type => :feature, :js => true do
|
|
21
21
|
|
22
22
|
it 'should show news stories', :js => true do
|
23
23
|
visit '/tumblr_api.html'
|
24
|
-
page.
|
25
|
-
page.
|
26
|
-
page.
|
27
|
-
page.
|
24
|
+
expect(page).to have_link('News Item 1', :href => 'http://example.com/news/1')
|
25
|
+
expect(page).to have_content('News item 1 content here')
|
26
|
+
expect(page).to have_link('News Item 2', :href => 'http://example.com/news/2')
|
27
|
+
expect(page).to have_content('News item 2 content here')
|
28
28
|
end
|
29
29
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Billy::Cache do
|
4
|
+
describe 'format_url' do
|
5
|
+
let(:cache) { Billy::Cache.new }
|
6
|
+
let(:params) { '?foo=bar' }
|
7
|
+
let(:fragment) { '#baz' }
|
8
|
+
let(:base_url) { 'http://example.com' }
|
9
|
+
let(:fragment_url) { "#{base_url}/#{fragment}" }
|
10
|
+
let(:params_url) { "#{base_url}#{params}" }
|
11
|
+
let(:params_fragment_url) { "#{base_url}#{params}#{fragment}" }
|
12
|
+
|
13
|
+
context 'with ignore_params set to false' do
|
14
|
+
it 'is a no-op if there are no params' do
|
15
|
+
expect(cache.format_url(base_url)).to eq base_url
|
16
|
+
end
|
17
|
+
it 'appends params if there are params' do
|
18
|
+
expect(cache.format_url(params_url)).to eq params_url
|
19
|
+
end
|
20
|
+
it 'appends params and fragment if both are present' do
|
21
|
+
expect(cache.format_url(params_fragment_url)).to eq params_fragment_url
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'with ignore_params set to true' do
|
26
|
+
it 'is a no-op if there are no params' do
|
27
|
+
expect(cache.format_url(base_url, true)).to eq base_url
|
28
|
+
end
|
29
|
+
it 'omits params if there are params' do
|
30
|
+
expect(cache.format_url(params_url, true)).to eq base_url
|
31
|
+
end
|
32
|
+
it 'omits params and fragment if both are present' do
|
33
|
+
expect(cache.format_url(params_fragment_url, true)).to eq base_url
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -3,32 +3,42 @@ require 'spec_helper'
|
|
3
3
|
describe Billy::ProxyRequestStub do
|
4
4
|
context '#matches?' do
|
5
5
|
it 'should match urls and methods' do
|
6
|
-
Billy::ProxyRequestStub.new('http://example.com').
|
7
|
-
matches?('GET', 'http://example.com').
|
8
|
-
Billy::ProxyRequestStub.new('http://example.com').
|
9
|
-
matches?('POST', 'http://example.com').
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
Billy::ProxyRequestStub.new('http://
|
17
|
-
matches?('POST', 'http://example.com').
|
6
|
+
expect(Billy::ProxyRequestStub.new('http://example.com').
|
7
|
+
matches?('GET', 'http://example.com')).to be
|
8
|
+
expect(Billy::ProxyRequestStub.new('http://example.com').
|
9
|
+
matches?('POST', 'http://example.com')).to_not be
|
10
|
+
|
11
|
+
expect(Billy::ProxyRequestStub.new('http://example.com', :method => :get).
|
12
|
+
matches?('GET', 'http://example.com')).to be
|
13
|
+
expect(Billy::ProxyRequestStub.new('http://example.com', :method => :post).
|
14
|
+
matches?('GET', 'http://example.com')).to_not be
|
15
|
+
|
16
|
+
expect(Billy::ProxyRequestStub.new('http://example.com', :method => :post).
|
17
|
+
matches?('POST', 'http://example.com')).to be
|
18
|
+
expect(Billy::ProxyRequestStub.new('http://fooxample.com', :method => :post).
|
19
|
+
matches?('POST', 'http://example.com')).to_not be
|
18
20
|
end
|
19
21
|
|
20
22
|
it 'should match regexps' do
|
21
|
-
Billy::ProxyRequestStub.new(/http:\/\/.+\.com/, :method => :post).
|
22
|
-
matches?('POST', 'http://example.com').
|
23
|
-
Billy::ProxyRequestStub.new(/http:\/\/.+\.co\.uk/, :method => :get).
|
24
|
-
matches?('GET', 'http://example.com').
|
23
|
+
expect(Billy::ProxyRequestStub.new(/http:\/\/.+\.com/, :method => :post).
|
24
|
+
matches?('POST', 'http://example.com')).to be
|
25
|
+
expect(Billy::ProxyRequestStub.new(/http:\/\/.+\.co\.uk/, :method => :get).
|
26
|
+
matches?('GET', 'http://example.com')).to_not be
|
25
27
|
end
|
26
28
|
|
27
29
|
it 'should match up to but not including query strings' do
|
28
30
|
stub = Billy::ProxyRequestStub.new('http://example.com/foo/bar/')
|
29
|
-
stub.matches?('GET', 'http://example.com/foo/').
|
30
|
-
stub.matches?('GET', 'http://example.com/foo/bar/').
|
31
|
-
stub.matches?('GET', 'http://example.com/foo/bar/?baz=bap').
|
31
|
+
expect(stub.matches?('GET', 'http://example.com/foo/')).to_not be
|
32
|
+
expect(stub.matches?('GET', 'http://example.com/foo/bar/')).to be
|
33
|
+
expect(stub.matches?('GET', 'http://example.com/foo/bar/?baz=bap')).to be
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "#call (without #and_return)" do
|
38
|
+
let(:subject) { Billy::ProxyRequestStub.new('url') }
|
39
|
+
|
40
|
+
it "returns a 204 empty response" do
|
41
|
+
expect(subject.call({}, {}, nil)).to eql [204, {"Content-Type" => "text/plain"}, ""]
|
32
42
|
end
|
33
43
|
end
|
34
44
|
|
@@ -37,7 +47,7 @@ describe Billy::ProxyRequestStub do
|
|
37
47
|
|
38
48
|
it 'should generate bare responses' do
|
39
49
|
subject.and_return :body => 'baz foo bar'
|
40
|
-
subject.call({}, {}, nil).
|
50
|
+
expect(subject.call({}, {}, nil)).to eql [
|
41
51
|
200,
|
42
52
|
{},
|
43
53
|
'baz foo bar'
|
@@ -46,7 +56,7 @@ describe Billy::ProxyRequestStub do
|
|
46
56
|
|
47
57
|
it 'should generate text responses' do
|
48
58
|
subject.and_return :text => 'foo bar baz'
|
49
|
-
subject.call({}, {}, nil).
|
59
|
+
expect(subject.call({}, {}, nil)).to eql [
|
50
60
|
200,
|
51
61
|
{'Content-Type' => 'text/plain'},
|
52
62
|
'foo bar baz'
|
@@ -55,7 +65,7 @@ describe Billy::ProxyRequestStub do
|
|
55
65
|
|
56
66
|
it 'should generate JSON responses' do
|
57
67
|
subject.and_return :json => { :foo => 'bar' }
|
58
|
-
subject.call({}, {}, nil).
|
68
|
+
expect(subject.call({}, {}, nil)).to eql [
|
59
69
|
200,
|
60
70
|
{'Content-Type' => 'application/json'},
|
61
71
|
'{"foo":"bar"}'
|
@@ -65,7 +75,7 @@ describe Billy::ProxyRequestStub do
|
|
65
75
|
context 'JSONP' do
|
66
76
|
it 'should generate JSONP responses' do
|
67
77
|
subject.and_return :jsonp => { :foo => 'bar' }
|
68
|
-
subject.call({ 'callback' => ['baz'] }, {}, nil).
|
78
|
+
expect(subject.call({ 'callback' => ['baz'] }, {}, nil)).to eql [
|
69
79
|
200,
|
70
80
|
{'Content-Type' => 'application/javascript'},
|
71
81
|
'baz({"foo":"bar"})'
|
@@ -74,7 +84,7 @@ describe Billy::ProxyRequestStub do
|
|
74
84
|
|
75
85
|
it 'should generate JSONP responses with custom callback parameter' do
|
76
86
|
subject.and_return :jsonp => { :foo => 'bar' }, :callback_param => 'cb'
|
77
|
-
subject.call({ 'cb' => ['bap'] }, {}, nil).
|
87
|
+
expect(subject.call({ 'cb' => ['bap'] }, {}, nil)).to eql [
|
78
88
|
200,
|
79
89
|
{'Content-Type' => 'application/javascript'},
|
80
90
|
'bap({"foo":"bar"})'
|
@@ -83,7 +93,7 @@ describe Billy::ProxyRequestStub do
|
|
83
93
|
|
84
94
|
it 'should generate JSONP responses with custom callback name' do
|
85
95
|
subject.and_return :jsonp => { :foo => 'bar' }, :callback => 'cb'
|
86
|
-
subject.call({}, {}, nil).
|
96
|
+
expect(subject.call({}, {}, nil)).to eql [
|
87
97
|
200,
|
88
98
|
{'Content-Type' => 'application/javascript'},
|
89
99
|
'cb({"foo":"bar"})'
|
@@ -93,7 +103,7 @@ describe Billy::ProxyRequestStub do
|
|
93
103
|
|
94
104
|
it 'should generate redirection responses' do
|
95
105
|
subject.and_return :redirect_to => 'http://example.com'
|
96
|
-
subject.call({}, {}, nil).
|
106
|
+
expect(subject.call({}, {}, nil)).to eql [
|
97
107
|
302,
|
98
108
|
{'Location' => 'http://example.com'},
|
99
109
|
nil
|
@@ -102,7 +112,7 @@ describe Billy::ProxyRequestStub do
|
|
102
112
|
|
103
113
|
it 'should set headers' do
|
104
114
|
subject.and_return :text => 'foo', :headers => {'HTTP-X-Foo' => 'bar'}
|
105
|
-
subject.call({}, {}, nil).
|
115
|
+
expect(subject.call({}, {}, nil)).to eql [
|
106
116
|
200,
|
107
117
|
{'Content-Type' => 'text/plain', 'HTTP-X-Foo' => 'bar'},
|
108
118
|
'foo'
|
@@ -111,7 +121,7 @@ describe Billy::ProxyRequestStub do
|
|
111
121
|
|
112
122
|
it 'should set status codes' do
|
113
123
|
subject.and_return :text => 'baz', :code => 410
|
114
|
-
subject.call({}, {}, nil).
|
124
|
+
expect(subject.call({}, {}, nil)).to eql [
|
115
125
|
410,
|
116
126
|
{'Content-Type' => 'text/plain'},
|
117
127
|
'baz'
|
@@ -124,12 +134,12 @@ describe Billy::ProxyRequestStub do
|
|
124
134
|
expected_body = 'body text'
|
125
135
|
|
126
136
|
subject.and_return(Proc.new { |params, headers, body|
|
127
|
-
params.
|
128
|
-
headers.
|
129
|
-
body.
|
137
|
+
expect(params).to eql expected_params
|
138
|
+
expect(headers).to eql expected_headers
|
139
|
+
expect(body).to eql 'body text'
|
130
140
|
{:code => 418, :text => 'success'}
|
131
141
|
})
|
132
|
-
subject.call(expected_params, expected_headers, expected_body).
|
142
|
+
expect(subject.call(expected_params, expected_headers, expected_body)).to eql [
|
133
143
|
418,
|
134
144
|
{'Content-Type' => 'text/plain'},
|
135
145
|
'success'
|