puffing-billy 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+
3
+ describe Billy::CacheHandler do
4
+ let(:handler) { Billy::CacheHandler.new }
5
+ let(:request) { {
6
+ method: 'post',
7
+ url: 'http://example.test:8080/index?some=param&callback=dynamicCallback5678',
8
+ headers: {'Accept-Encoding' => 'gzip',
9
+ 'Cache-Control' => 'no-cache' },
10
+ body: 'Some body'
11
+ } }
12
+
13
+ it 'delegates #reset to the cache' do
14
+ expect(Billy::Cache.instance).to receive(:reset).at_least(:once)
15
+ handler.reset
16
+ end
17
+
18
+ it 'delegates #cached? to the cache' do
19
+ expect(Billy::Cache.instance).to receive :cached?
20
+ handler.cached?
21
+ end
22
+
23
+ describe '#handles_request?' do
24
+ it 'handles the request if it is cached' do
25
+ expect(Billy::Cache.instance).to receive(:cached?).and_return(true)
26
+ expect(handler.handles_request?(nil,nil,nil,nil)).to be true
27
+ end
28
+
29
+ it 'does not handle the request if it is not cached' do
30
+ expect(Billy::Cache.instance).to receive(:cached?).and_return(false)
31
+ expect(handler.handles_request?(nil,nil,nil,nil)).to be false
32
+ end
33
+ end
34
+
35
+ describe '#handle_request' do
36
+ it 'returns nil if the request cannot be handled' do
37
+ expect(Billy::Cache.instance).to receive(:cached?).and_return(false)
38
+ expect(handler.handle_request(request[:method],
39
+ request[:url],
40
+ request[:headers],
41
+ request[:body])).to be nil
42
+ end
43
+
44
+ it 'returns a cached response if the request can be handled' do
45
+ expect(Billy::Cache.instance).to receive(:cached?).and_return(true)
46
+ expect(Billy::Cache.instance).to receive(:fetch).and_return({:status=>200, :headers=>{"Connection"=>"close"}, :content=>"The response body"})
47
+ expect(handler.handle_request(request[:method],
48
+ request[:url],
49
+ request[:headers],
50
+ request[:body])).to eql({:status=>200, :headers=>{"Connection"=>"close"}, :content=>"The response body"})
51
+ end
52
+
53
+ context 'updating jsonp callback names enabled' do
54
+ before do
55
+ Billy.config.dynamic_jsonp = true
56
+ end
57
+
58
+ it 'updates the cached response if the callback is dynamic' do
59
+ expect(Billy::Cache.instance).to receive(:cached?).and_return(true)
60
+ expect(Billy::Cache.instance).to receive(:fetch).and_return({:status=>200, :headers=>{"Connection"=>"close"}, :content=> 'dynamicCallback1234({"yolo":"kitten"})'})
61
+ expect(handler.handle_request(request[:method],
62
+ request[:url],
63
+ request[:headers],
64
+ request[:body])).to eql({:status=>200, :headers=>{"Connection"=>"close"}, :content=>'dynamicCallback5678({"yolo":"kitten"})'})
65
+ end
66
+
67
+ it 'is flexible about the format of the response body' do
68
+ expect(Billy::Cache.instance).to receive(:cached?).and_return(true)
69
+ expect(Billy::Cache.instance).to receive(:fetch).and_return({:status=>200, :headers=>{"Connection"=>"close"}, :content=> "/**/ dynamicCallback1234(\n{\"yolo\":\"kitten\"})"})
70
+ expect(handler.handle_request(request[:method],
71
+ request[:url],
72
+ request[:headers],
73
+ request[:body])).to eql({:status=>200, :headers=>{"Connection"=>"close"}, :content=>"/**/ dynamicCallback5678(\n{\"yolo\":\"kitten\"})"})
74
+ end
75
+
76
+ it 'does not interfere with non-jsonp requests' do
77
+ jsonp_request = request
78
+ other_request = {
79
+ method: 'get',
80
+ url: 'http://example.test:8080/index?hanukkah=latkes',
81
+ headers: {'Accept-Encoding'=>'gzip', 'Cache-Control'=>'no-cache' },
82
+ body: 'no jsonp'
83
+ }
84
+
85
+ allow(Billy::Cache.instance).to receive(:cached?).and_return(true)
86
+ allow(Billy::Cache.instance).to receive(:fetch).with(jsonp_request[:method], jsonp_request[:url], jsonp_request[:body]).and_return({:status=>200,
87
+ :headers=>{"Connection"=>"close"},
88
+ :content=> 'dynamicCallback1234({"yolo":"kitten"})'})
89
+ allow(Billy::Cache.instance).to receive(:fetch).with(other_request[:method], other_request[:url], other_request[:body]).and_return({:status=>200,
90
+ :headers=>{"Connection"=>"close"},
91
+ :content=> 'no jsonp but has parentheses()'})
92
+
93
+ expect(handler.handle_request(other_request[:method],
94
+ other_request[:url],
95
+ other_request[:headers],
96
+ other_request[:body])).to eql({:status=>200, :headers=>{"Connection"=>"close"}, :content=>'no jsonp but has parentheses()'})
97
+ end
98
+ end
99
+
100
+ context 'updating jsonp callback names disabled' do
101
+ before do
102
+ Billy.config.dynamic_jsonp = false
103
+ end
104
+
105
+ it 'does not change the response' do
106
+ expect(Billy::Cache.instance).to receive(:cached?).and_return(true)
107
+ expect(Billy::Cache.instance).to receive(:fetch).and_return({:status=>200, :headers=>{"Connection"=>"close"}, :content=> 'dynamicCallback1234({"yolo":"kitten"})'})
108
+ expect(handler.handle_request(request[:method],
109
+ request[:url],
110
+ request[:headers],
111
+ request[:body])).to eql({:status=>200, :headers=>{"Connection"=>"close"}, :content=>'dynamicCallback1234({"yolo":"kitten"})'})
112
+ end
113
+ end
114
+
115
+ it 'returns nil if the Cache fails to handle the response for some reason' do
116
+ expect(Billy::Cache.instance).to receive(:cached?).and_return(true)
117
+ expect(Billy::Cache.instance).to receive(:fetch).and_return(nil)
118
+ expect(handler.handle_request(request[:method],
119
+ request[:url],
120
+ request[:headers],
121
+ request[:body])).to be nil
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe Billy::Handler do
4
+ let(:handler) { Class.new{ include Billy::Handler }.new }
5
+ it '#handle_request raises an error if not overridden' do
6
+ expect(handler.handle_request(nil,nil,nil,nil)).to eql({ error: 'The handler has not overridden the handle_request method!' })
7
+ end
8
+
9
+ it '#handles_request returns false by default' do
10
+ expect(handler.handles_request?(nil,nil,nil,nil)).to be false
11
+ end
12
+
13
+ it 'responds to #reset' do
14
+ expect(handler).to respond_to :reset
15
+ end
16
+ end
@@ -0,0 +1,171 @@
1
+ require 'spec_helper'
2
+
3
+ describe Billy::ProxyHandler do
4
+ subject { Billy::ProxyHandler.new }
5
+ let(:request) { {
6
+ method: 'post',
7
+ url: 'http://example.test:8080/index?some=param',
8
+ headers: {'Accept-Encoding' => 'gzip',
9
+ 'Cache-Control' => 'no-cache' },
10
+ body: 'Some body'
11
+ } }
12
+
13
+ describe '#handles_request?' do
14
+ context 'with non-whitelisted requests enabled' do
15
+ before do
16
+ expect(Billy.config).to receive(:non_whitelisted_requests_disabled).and_return(false)
17
+ end
18
+
19
+ it 'handles all requests' do
20
+ expect(subject.handles_request?(request[:method],
21
+ request[:url],
22
+ request[:headers],
23
+ request[:body])).to be true
24
+ end
25
+ end
26
+ context 'with non-whitelisted requests disabled' do
27
+ before do
28
+ expect(Billy.config).to receive(:non_whitelisted_requests_disabled).and_return(true)
29
+ end
30
+
31
+ it 'does not handle requests that are not white or black listed' do
32
+ expect(subject.handles_request?(request[:method],
33
+ request[:url],
34
+ request[:headers],
35
+ request[:body])).to be false
36
+ end
37
+
38
+ context 'a whitelisted host' do
39
+ context 'with a blacklisted path' do
40
+ before do
41
+ expect(Billy.config).to receive(:path_blacklist) { ['/index'] }
42
+ end
43
+
44
+ it 'does not handle requests for blacklisted paths' do
45
+ expect(subject.handles_request?(request[:method],
46
+ 'http://example.test:8080/index?some=param',
47
+ request[:headers],
48
+ request[:body])).to be false
49
+ end
50
+ end
51
+ context 'without a port' do
52
+ before do
53
+ expect(Billy.config).to receive(:whitelist) { ['example.test'] }
54
+ end
55
+
56
+ it 'handles requests for the host without a port' do
57
+ expect(subject.handles_request?(request[:method],
58
+ 'http://example.test',
59
+ request[:headers],
60
+ request[:body])).to be true
61
+ end
62
+
63
+ it 'handles requests for the host with a port' do
64
+ expect(subject.handles_request?(request[:method],
65
+ 'http://example.test:8080',
66
+ request[:headers],
67
+ request[:body])).to be true
68
+ end
69
+ end
70
+
71
+ context 'with a port' do
72
+ before do
73
+ expect(Billy.config).to receive(:whitelist) { ['example.test:8080'] }
74
+ end
75
+
76
+ it 'does not handle requests whitelisted for a specific port' do
77
+ expect(subject.handles_request?(request[:method],
78
+ 'http://example.test',
79
+ request[:headers],
80
+ request[:body])).to be false
81
+ end
82
+
83
+ it 'handles requests for the host with a port' do
84
+ expect(subject.handles_request?(request[:method],
85
+ 'http://example.test:8080',
86
+ request[:headers],
87
+ request[:body])).to be true
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ describe '#handle_request' do
95
+ it 'returns nil if it does not handle the request' do
96
+ expect(subject).to receive(:handles_request?).and_return(false)
97
+ expect(subject.handle_request(request[:method],
98
+ request[:url],
99
+ request[:headers],
100
+ request[:body])).to be nil
101
+ end
102
+
103
+ context 'with a handled request' do
104
+ let(:response_header) do
105
+ header = Struct.new(:status, :raw).new
106
+ header.status = 200
107
+ header.raw = {}
108
+ header
109
+ end
110
+
111
+ let(:em_response) { double("response") }
112
+ let(:em_request) {
113
+ double("EM::HttpRequest", :error => nil, :response => em_response, :response_header => response_header)
114
+ }
115
+
116
+ before do
117
+ allow(subject).to receive(:handles_request?).and_return(true)
118
+ allow(em_response).to receive(:force_encoding).and_return("The response body")
119
+ allow(EventMachine::HttpRequest).to receive(:new).and_return(em_request)
120
+ expect(em_request).to receive(:post).and_return(em_request)
121
+ end
122
+
123
+ it 'returns any error in the response' do
124
+ allow(em_request).to receive(:error).and_return("ERROR!")
125
+ expect(subject.handle_request(request[:method],
126
+ request[:url],
127
+ request[:headers],
128
+ request[:body])).to eql({ :error => "Request to #{request[:url]} failed with error: ERROR!"})
129
+ end
130
+
131
+ it 'returns a hashed response if the request succeeds' do
132
+ expect(subject.handle_request(request[:method],
133
+ request[:url],
134
+ request[:headers],
135
+ request[:body])).to eql({:status=>200, :headers=>{"Connection"=>"close"}, :content=>"The response body"})
136
+ end
137
+
138
+ it 'returns nil if both the error and response are for some reason nil' do
139
+ allow(em_request).to receive(:response).and_return(nil)
140
+ expect(subject.handle_request(request[:method],
141
+ request[:url],
142
+ request[:headers],
143
+ request[:body])).to be nil
144
+ end
145
+
146
+ it 'caches the response if cacheable' do
147
+ expect(subject).to receive(:allowed_response_code?).and_return(true)
148
+ expect(Billy::Cache.instance).to receive(:store)
149
+ subject.handle_request(request[:method],
150
+ request[:url],
151
+ request[:headers],
152
+ request[:body])
153
+ end
154
+
155
+ it 'uses the timeouts defined in configuration' do
156
+ allow(Billy.config).to receive(:proxied_request_inactivity_timeout).and_return(42)
157
+ allow(Billy.config).to receive(:proxied_request_connect_timeout).and_return(24)
158
+
159
+ expect(EventMachine::HttpRequest).to receive(:new).with(request[:url], {
160
+ inactivity_timeout: 42,
161
+ connect_timeout: 24
162
+ })
163
+
164
+ subject.handle_request(request[:method],
165
+ request[:url],
166
+ request[:headers],
167
+ request[:body])
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,144 @@
1
+ require 'spec_helper'
2
+
3
+ describe Billy::RequestHandler do
4
+ subject { Billy::RequestHandler.new }
5
+
6
+ it 'implements Handler' do
7
+ expect(subject).to be_a Billy::Handler
8
+ end
9
+
10
+ describe '#handlers' do
11
+ it 'has a stub handler' do
12
+ expect(subject.handlers[:stubs]).to be_a Billy::StubHandler
13
+ end
14
+
15
+ it 'has a cache handler' do
16
+ expect(subject.handlers[:cache]).to be_a Billy::CacheHandler
17
+ end
18
+
19
+ it 'has a proxy handler' do
20
+ expect(subject.handlers[:proxy]).to be_a Billy::ProxyHandler
21
+ end
22
+ end
23
+
24
+ context 'with stubbed handlers' do
25
+ let(:args) { ["get","url","headers","body"] }
26
+ let(:stub_handler) { double("StubHandler") }
27
+ let(:cache_handler) { double("CacheHandler") }
28
+ let(:proxy_handler) { double("ProxyHandler") }
29
+ let(:handlers) { {
30
+ :stubs => stub_handler,
31
+ :cache => cache_handler,
32
+ :proxy => proxy_handler
33
+ } }
34
+
35
+ before do
36
+ allow(subject).to receive(:handlers).and_return(handlers)
37
+ end
38
+
39
+ describe '#handles_request?' do
40
+ it 'returns false if no handlers handle the request' do
41
+ handlers.each do |key,handler|
42
+ expect(handler).to receive(:handles_request?).with(*args).and_return(false)
43
+ end
44
+ expect(subject.handles_request?(*args)).to be false
45
+ end
46
+
47
+ it 'returns true immediately if the stub handler handles the request' do
48
+ expect(stub_handler).to receive(:handles_request?).with(*args).and_return(true)
49
+ expect(cache_handler).to_not receive(:handles_request?)
50
+ expect(proxy_handler).to_not receive(:handles_request?)
51
+ expect(subject.handles_request?(*args)).to be true
52
+ end
53
+
54
+ it 'returns true if the cache handler handles the request' do
55
+ expect(stub_handler).to receive(:handles_request?).with(*args).and_return(false)
56
+ expect(cache_handler).to receive(:handles_request?).with(*args).and_return(true)
57
+ expect(proxy_handler).to_not receive(:handles_request?)
58
+ expect(subject.handles_request?(*args)).to be true
59
+ end
60
+
61
+ it 'returns true if the proxy handler handles the request' do
62
+ expect(stub_handler).to receive(:handles_request?).with(*args).and_return(false)
63
+ expect(cache_handler).to receive(:handles_request?).with(*args).and_return(false)
64
+ expect(proxy_handler).to receive(:handles_request?).with(*args).and_return(true)
65
+ expect(subject.handles_request?(*args)).to be true
66
+ end
67
+ end
68
+
69
+ describe '#handle_request' do
70
+ it 'returns stubbed responses' do
71
+ expect(stub_handler).to receive(:handle_request).with(*args).and_return("foo")
72
+ expect(cache_handler).to_not receive(:handle_request)
73
+ expect(proxy_handler).to_not receive(:handle_request)
74
+ expect(subject.handle_request(*args)).to eql "foo"
75
+ end
76
+
77
+ it 'returns cached responses' do
78
+ expect(stub_handler).to receive(:handle_request).with(*args)
79
+ expect(cache_handler).to receive(:handle_request).with(*args).and_return("bar")
80
+ expect(proxy_handler).to_not receive(:handle_request)
81
+ expect(subject.handle_request(*args)).to eql "bar"
82
+ end
83
+
84
+ it 'returns proxied responses' do
85
+ expect(stub_handler).to receive(:handle_request).with(*args)
86
+ expect(cache_handler).to receive(:handle_request).with(*args)
87
+ expect(proxy_handler).to receive(:handle_request).with(*args).and_return("baz")
88
+ expect(subject.handle_request(*args)).to eql "baz"
89
+ end
90
+
91
+ it 'returns an error hash if request is not handled' do
92
+ expect(stub_handler).to receive(:handle_request).with(*args)
93
+ expect(cache_handler).to receive(:handle_request).with(*args)
94
+ expect(proxy_handler).to receive(:handle_request).with(*args)
95
+ expect(subject.handle_request(*args)).to eql({ :error => "Connection to url not cached and new http connections are disabled" })
96
+ end
97
+
98
+ it 'returns an error hash with body message if POST request is not handled' do
99
+ args[0] = 'post'
100
+ expect(stub_handler).to receive(:handle_request).with(*args)
101
+ expect(cache_handler).to receive(:handle_request).with(*args)
102
+ expect(proxy_handler).to receive(:handle_request).with(*args)
103
+ expect(subject.handle_request(*args)).to eql({ :error => "Connection to url with body 'body' not cached and new http connections are disabled" })
104
+ end
105
+ end
106
+
107
+ describe '#stub' do
108
+ it 'delegates to the stub_handler' do
109
+ expect(stub_handler).to receive(:stub).with("some args")
110
+ subject.stub("some args")
111
+ end
112
+ end
113
+
114
+ describe '#reset' do
115
+ it 'resets all of the handlers' do
116
+ handlers.each do |key,handler|
117
+ expect(handler).to receive(:reset)
118
+ end
119
+ subject.reset
120
+ end
121
+ end
122
+
123
+ describe '#reset_stubs' do
124
+ it 'resets the stub handler' do
125
+ expect(stub_handler).to receive(:reset)
126
+ subject.reset_stubs
127
+ end
128
+ end
129
+
130
+ describe '#reset_cache' do
131
+ it 'resets the cache handler' do
132
+ expect(cache_handler).to receive(:reset)
133
+ subject.reset_cache
134
+ end
135
+ end
136
+
137
+ describe '#restore_cache' do
138
+ it 'resets the cache handler' do
139
+ expect(cache_handler).to receive(:reset)
140
+ subject.reset_cache
141
+ end
142
+ end
143
+ end
144
+ end