sitehub 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +6 -0
  5. data/Gemfile.lock +114 -0
  6. data/LICENSE +28 -0
  7. data/README.md +198 -0
  8. data/Rakefile +11 -0
  9. data/lib/sitehub.rb +9 -0
  10. data/lib/sitehub/builder.rb +82 -0
  11. data/lib/sitehub/collection.rb +35 -0
  12. data/lib/sitehub/collection/route_collection.rb +28 -0
  13. data/lib/sitehub/collection/split_route_collection.rb +50 -0
  14. data/lib/sitehub/collection/split_route_collection/split.rb +18 -0
  15. data/lib/sitehub/constants.rb +23 -0
  16. data/lib/sitehub/constants/http_header_keys.rb +25 -0
  17. data/lib/sitehub/constants/rack_http_header_keys.rb +17 -0
  18. data/lib/sitehub/cookie.rb +54 -0
  19. data/lib/sitehub/cookie/attribute.rb +22 -0
  20. data/lib/sitehub/cookie/flag.rb +22 -0
  21. data/lib/sitehub/cookie_rewriting.rb +35 -0
  22. data/lib/sitehub/forward_proxies.rb +50 -0
  23. data/lib/sitehub/forward_proxy.rb +67 -0
  24. data/lib/sitehub/forward_proxy_builder.rb +99 -0
  25. data/lib/sitehub/http_headers.rb +60 -0
  26. data/lib/sitehub/logging.rb +5 -0
  27. data/lib/sitehub/logging/access_logger.rb +74 -0
  28. data/lib/sitehub/logging/error_logger.rb +36 -0
  29. data/lib/sitehub/logging/log_entry.rb +14 -0
  30. data/lib/sitehub/logging/log_stash.rb +10 -0
  31. data/lib/sitehub/logging/log_wrapper.rb +23 -0
  32. data/lib/sitehub/middleware.rb +21 -0
  33. data/lib/sitehub/path_directive.rb +32 -0
  34. data/lib/sitehub/path_directives.rb +21 -0
  35. data/lib/sitehub/request_mapping.rb +43 -0
  36. data/lib/sitehub/resolver.rb +11 -0
  37. data/lib/sitehub/reverse_proxy.rb +53 -0
  38. data/lib/sitehub/rules.rb +13 -0
  39. data/lib/sitehub/string_sanitiser.rb +7 -0
  40. data/lib/sitehub/transaction_id.rb +16 -0
  41. data/lib/sitehub/version.rb +3 -0
  42. data/mem_usage.txt +1584 -0
  43. data/sitehub.gemspec +43 -0
  44. data/spec/basket_spec.rb +30 -0
  45. data/spec/sitehub/builder_spec.rb +203 -0
  46. data/spec/sitehub/collection/route_collection_spec.rb +91 -0
  47. data/spec/sitehub/collection/split_route_collection_spec.rb +111 -0
  48. data/spec/sitehub/collection_spec.rb +40 -0
  49. data/spec/sitehub/cookie/attribute_spec.rb +37 -0
  50. data/spec/sitehub/cookie/flag_spec.rb +27 -0
  51. data/spec/sitehub/cookie_rewriting_spec.rb +67 -0
  52. data/spec/sitehub/cookie_spec.rb +61 -0
  53. data/spec/sitehub/error_handling_spec.rb +21 -0
  54. data/spec/sitehub/forward_proxies_spec.rb +99 -0
  55. data/spec/sitehub/forward_proxy_builder_spec.rb +295 -0
  56. data/spec/sitehub/forward_proxy_spec.rb +138 -0
  57. data/spec/sitehub/http_headers_spec.rb +71 -0
  58. data/spec/sitehub/integration_spec.rb +21 -0
  59. data/spec/sitehub/logging/access_logger_spec.rb +127 -0
  60. data/spec/sitehub/logging/error_logger_spec.rb +80 -0
  61. data/spec/sitehub/logging/log_entry_spec.rb +34 -0
  62. data/spec/sitehub/logging/log_stash_spec.rb +21 -0
  63. data/spec/sitehub/logging/log_wrapper_spec.rb +33 -0
  64. data/spec/sitehub/middleware_spec.rb +69 -0
  65. data/spec/sitehub/path_directive_spec.rb +50 -0
  66. data/spec/sitehub/path_directives_spec.rb +45 -0
  67. data/spec/sitehub/request_mapping_spec.rb +71 -0
  68. data/spec/sitehub/resolver_spec.rb +15 -0
  69. data/spec/sitehub/reverse_proxy_spec.rb +105 -0
  70. data/spec/sitehub/transaction_id_spec.rb +28 -0
  71. data/spec/sitehub_spec.rb +19 -0
  72. data/spec/spec_helper.rb +26 -0
  73. data/spec/support/patch/rack/response.rb +25 -0
  74. data/spec/support/shared_contexts/async_context.rb +69 -0
  75. data/spec/support/shared_contexts/middleware_context.rb +51 -0
  76. data/spec/support/shared_contexts/rack_test_context.rb +12 -0
  77. data/spec/support/shared_contexts/sitehub_context.rb +25 -0
  78. data/spec/support/silent_warnings.rb +5 -0
  79. metadata +359 -0
@@ -0,0 +1,40 @@
1
+ require 'sitehub/collection'
2
+
3
+ class SiteHub
4
+ describe Collection do
5
+ describe '#valid?' do
6
+ it 'must be overiden and raises an exception by default' do
7
+ expect { subject.valid? }.to raise_exception "implement me"
8
+ end
9
+ end
10
+
11
+ describe '#resolve' do
12
+ it 'must be overiden and raises an exception by default' do
13
+ expect { subject.resolve }.to raise_exception "implement me"
14
+ end
15
+ end
16
+
17
+ describe '.inherited' do
18
+ describe '#add' do
19
+ context 'duplicate ids added' do
20
+ subject do
21
+ inheritor = Class.new(described_class) do
22
+ def add id, value, *args
23
+ self[id] = value
24
+ end
25
+ end
26
+
27
+ inheritor.new
28
+ end
29
+ it 'raises an error' do
30
+ duplicate = Struct.new(:id).new(1)
31
+ subject.add(duplicate.id, duplicate)
32
+ expect{subject.add(duplicate.id, duplicate)}.to raise_exception described_class::DuplicateVersionException, 'supply unique labels'
33
+ end
34
+ end
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,37 @@
1
+ class SiteHub
2
+ class Cookie
3
+ describe Attribute do
4
+ let(:attribute_name){'domain'}
5
+ let(:attribute_value){'value'}
6
+ subject do
7
+ described_class.new(attribute_name, attribute_value)
8
+ end
9
+ describe '#initialize' do
10
+ it 'stores the attribute name and its value' do
11
+ expect(subject.name).to eq(attribute_name.to_sym)
12
+ expect(subject.value).to eq(attribute_value)
13
+ end
14
+
15
+ it 'sanitises the string parameter' do
16
+ expect(described_class.new("#{attribute_name} \n", attribute_value).name).to eq(attribute_name.to_sym)
17
+ end
18
+
19
+ context 'value is nil' do
20
+ subject do
21
+ described_class.new(attribute_name, nil)
22
+ end
23
+
24
+ it 'default to empty string' do
25
+ expect(subject.value).to eq('')
26
+ end
27
+ end
28
+ end
29
+
30
+ describe '#to_s' do
31
+ it 'contains the attribute name and value' do
32
+ expect(subject.to_s).to eq("#{attribute_name}=#{attribute_value}")
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,27 @@
1
+ class SiteHub
2
+ class Cookie
3
+ describe Flag do
4
+ let(:name) { 'flag' }
5
+ let(:string_sanitiser){Object.new.tap{|o|o.extend(StringSanitiser)}}
6
+ subject do
7
+ described_class.new(name)
8
+ end
9
+
10
+ describe '#initialize' do
11
+ it 'stores the value as a symbol' do
12
+ expect(subject.name).to eq(name.to_sym)
13
+ end
14
+
15
+ it 'sanitises the string parameter' do
16
+ expect(described_class.new("#{name} \n").name).to eq(name.to_sym)
17
+ end
18
+ end
19
+
20
+ describe '#to_s' do
21
+ it 'returns the name' do
22
+ expect(subject.to_s).to eq(name)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,67 @@
1
+ require 'sitehub/cookie_rewriting'
2
+
3
+ class SiteHub
4
+ describe CookieRewriting do
5
+ let(:downstream_domain){'.downstream_domain.com'}
6
+ let(:request_mapping){RequestMapping.new(source_url: 'http://example.org', mapped_url: "http://#{downstream_domain}", mapped_path: '/map')}
7
+ let(:substitute_domain){URI(request_mapping.source_url).host}
8
+ let(:substitute_path){'/path'}
9
+ let(:downstream_response){Rack::Response.new}
10
+ let(:downstream_domain_cookie_name){'downstream.cookie'}
11
+
12
+ subject do
13
+ Object.new.tap do |o|
14
+ o.extend(described_class)
15
+ end
16
+ end
17
+
18
+ before do
19
+ downstream_response.set_cookie(downstream_domain_cookie_name, domain: downstream_domain, value: 'value')
20
+ downstream_response.set_cookie('downstream.cookie2', domain: downstream_domain, value: 'value2', httponly: true)
21
+ end
22
+
23
+ describe '#cookies_string_as_hash' do
24
+ it 'returns the string as a hash' do
25
+ cookie_header = downstream_response.headers['Set-Cookie']
26
+
27
+ cookie_strings = cookie_header.lines
28
+ cookie1 = SiteHub::Cookie.new(cookie_strings[0])
29
+ cookie2 = SiteHub::Cookie.new(cookie_strings[1])
30
+ expected = {
31
+ cookie1.name => cookie1,
32
+ cookie2.name => cookie2
33
+ }
34
+ result = subject.cookies_string_as_hash(cookie_header)
35
+ expect(result).to eq(expected)
36
+ end
37
+ end
38
+
39
+ describe '#cookies_hash_to_string' do
40
+ it 'returns the hash as a correctly formatted string' do
41
+ cookies_hash = subject.cookies_string_as_hash(downstream_response.headers['Set-Cookie'])
42
+ expect(subject.cookies_hash_to_string(cookies_hash)).to eq(downstream_response.headers['Set-Cookie'])
43
+ end
44
+ end
45
+
46
+ describe '#rewrite_cookies' do
47
+
48
+ context 'subdomain character present' do
49
+ it 'substitues the domain for the mapped domain' do
50
+ downstream_response.set_cookie(downstream_domain_cookie_name, domain: downstream_domain, value: 'value')
51
+ subject.rewrite_cookies(downstream_response.headers, substitute_domain: substitute_domain)
52
+ expect(downstream_response.cookies[downstream_domain_cookie_name][:domain]).to eq('.example.org')
53
+ end
54
+ end
55
+
56
+ context 'subdomain not character present' do
57
+ it 'substitues the domain for the mapped domain' do
58
+ downstream_response.set_cookie(downstream_domain_cookie_name, domain: 'downstream.com', value: 'value')
59
+ subject.rewrite_cookies(downstream_response.headers, substitute_domain: substitute_domain)
60
+ expect(downstream_response.cookies[downstream_domain_cookie_name][:domain]).to eq('example.org')
61
+ end
62
+ end
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,61 @@
1
+ require 'sitehub/cookie'
2
+
3
+ class SiteHub
4
+ describe Cookie do
5
+ let(:attribute_class){described_class::Attribute}
6
+ let(:flag_class){described_class::Flag}
7
+ let(:domain_attribute){attribute_class.new(:domain.to_s, 'example.com')}
8
+ let(:name){'sitehub.recorded_route'}
9
+ let(:value){'new'}
10
+ let(:cookie_string){ "#{name}=#{value}; HttpOnly; #{domain_attribute}"}
11
+
12
+ subject do
13
+ described_class.new cookie_string
14
+ end
15
+
16
+ describe '#initialize' do
17
+ it 'parse a cookie string in to attributes and flags' do
18
+ expect(subject.attributes_and_flags).to eq([flag_class.new('HttpOnly'), domain_attribute])
19
+ end
20
+
21
+ it 'extracts name as a symbol' do
22
+ expect(subject.name).to eq('sitehub.recorded_route'.to_sym)
23
+ end
24
+
25
+ it 'extracts the value' do
26
+ expect(subject.value).to eq('new')
27
+ end
28
+ end
29
+
30
+
31
+ describe '#find' do
32
+
33
+ context 'entry found' do
34
+ it 'returns the entry with the given name' do
35
+ expect(subject.find(:domain)).to eq(domain_attribute)
36
+ end
37
+ end
38
+
39
+ context 'entry not found' do
40
+ it 'returns nil' do
41
+ expect(subject.find(:missing)).to eq(nil)
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ describe '#delete' do
48
+ it 'removes the entry' do
49
+ subject.delete(:domain)
50
+ expect(subject.find(:domain)).to eq(nil)
51
+ end
52
+ end
53
+
54
+ describe '#to_s' do
55
+ it 'prints the attributes and flags' do
56
+ expect(subject.to_s).to eq(cookie_string)
57
+ end
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1,21 @@
1
+ describe 'error handling' do
2
+ include_context :site_hub
3
+ include_context :async
4
+
5
+
6
+ before do
7
+ WebMock.enable!
8
+ end
9
+ context 'connectivity error' do
10
+ def app
11
+ @app||= async_middleware.new(rack_application)
12
+ ensure
13
+ WebMock.disable!
14
+ end
15
+
16
+ it 'shows the error page when an exception occurs' do
17
+ get('/endpoint')
18
+ expect(last_response.status).to eq(500)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,99 @@
1
+ require 'sitehub/forward_proxies'
2
+
3
+ class SiteHub
4
+ describe ForwardProxies do
5
+
6
+ let(:base_url) { 'http://google.com' }
7
+ let(:application_root) { '/application_url' }
8
+ let(:forward_proxy_builder) do
9
+ ForwardProxyBuilder.new(mapped_path: application_root).tap do |builder|
10
+ builder.split url: base_url, label: :current, percentage: 100
11
+ end
12
+ end
13
+
14
+ subject do
15
+ described_class.new.tap do |route_set|
16
+ route_set << forward_proxy_builder
17
+ end
18
+ end
19
+
20
+ before do
21
+ subject.init
22
+ end
23
+
24
+ describe '#init' do
25
+ it 'builds all of the forward_proxies' do
26
+ expect(subject.forward_proxies[application_root]).to receive(:build).and_call_original
27
+ subject.init
28
+ end
29
+ end
30
+
31
+ describe '#mapped_route' do
32
+
33
+ let(:request) { Rack::Request.new({}) }
34
+ let(:more_specific_proxy_builder) {ForwardProxyBuilder.new(url: "#{base_url}/sub_url", mapped_path: "#{application_root}/sub_url")}
35
+
36
+ context 'regex match on path' do
37
+ let(:fuzzy_matcher){ ForwardProxyBuilder.new(url: "#{base_url}/$1/view", mapped_path: %r{#{application_root}/(.*)/view})}
38
+ subject do
39
+ described_class.new.tap do |route_set|
40
+
41
+ route_set << fuzzy_matcher
42
+ end
43
+ end
44
+
45
+ it 'matches and subsitutes the captured group' do
46
+ expect(subject.mapped_route(path: "#{application_root}/123/view", request: request)).to eq(fuzzy_matcher.resolve(env:{}))
47
+ end
48
+ end
49
+
50
+ context 'exact match on path' do
51
+ it 'proxies to the requested path' do
52
+ expect(subject.mapped_route(path: application_root, request: request)).to eq(forward_proxy_builder.resolve(env:{}))
53
+ end
54
+ end
55
+
56
+ context 'when more specific route is configured first' do
57
+ subject do
58
+ described_class.new.tap do |route_set|
59
+ route_set << more_specific_proxy_builder
60
+ route_set << forward_proxy_builder
61
+ end
62
+ end
63
+
64
+ it 'matches the first endpoint' do
65
+ expect(subject.mapped_route(path: "#{application_root}/sub_url", request: request)).to eq(more_specific_proxy_builder.resolve(env:{}))
66
+ end
67
+ end
68
+ end
69
+
70
+
71
+ describe '#call' do
72
+
73
+ context 'mapped_route not found' do
74
+ let(:app) do
75
+ subject
76
+ end
77
+
78
+ it 'returns a 404' do
79
+ expect(get('/missing').status).to eq(404)
80
+ end
81
+ end
82
+
83
+ context 'mapped_route found' do
84
+ let(:app) do
85
+ subject
86
+ end
87
+
88
+ it 'uses the forward proxy' do
89
+ subject
90
+ expect(forward_proxy_builder.endpoints[:current]).to receive(:call) do
91
+ [200, {}, []]
92
+ end
93
+ expect(get(application_root).status).to eq(200)
94
+ end
95
+ end
96
+ end
97
+
98
+ end
99
+ end
@@ -0,0 +1,295 @@
1
+ require 'sitehub/forward_proxy_builder'
2
+
3
+ class SiteHub
4
+
5
+ describe ForwardProxyBuilder do
6
+
7
+ include_context :middleware_test
8
+
9
+ subject do
10
+ described_class.new(mapped_path: '/path')
11
+ end
12
+
13
+ it 'supports middleware' do
14
+ expect(described_class).to include(Middleware)
15
+ end
16
+
17
+ describe '#initialize' do
18
+ context 'with a block' do
19
+ it 'evaluates the block in the context of the instance' do
20
+ self_inside_block = nil
21
+ instance = described_class.new(url: '/app1', mapped_path: '/path') { self_inside_block = self }
22
+ expect(self_inside_block).to eq(instance)
23
+ end
24
+ end
25
+ end
26
+
27
+ describe 'valid?' do
28
+ context 'default defined' do
29
+ context 'splits add up to 100' do
30
+ it 'returns true' do
31
+ subject.split(percentage: 100, label: :old, url: :url)
32
+ expect(subject.valid?).to eq(true)
33
+ end
34
+ end
35
+
36
+ context 'splits do not add up to 100' do
37
+ it 'returns true' do
38
+ subject.split(percentage: 10, label: :old, url: :url)
39
+ subject.default(url: :url)
40
+ expect(subject.valid?).to eq(true)
41
+ end
42
+ end
43
+ end
44
+
45
+ context 'default not defined' do
46
+ context 'splits add up to 100' do
47
+ it 'returns true' do
48
+ subject.default(url: :url)
49
+ expect(subject.valid?).to eq(true)
50
+ end
51
+ end
52
+
53
+ context 'splits do not add up to 100' do
54
+ it 'returns false' do
55
+ subject.split(percentage: 10, label: :old, url: :url)
56
+ subject.split(percentage: 10, label: :new, url: :url)
57
+ expect(subject).to_not be_valid
58
+ end
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+ describe '#split' do
65
+ context 'duplicate label used' do
66
+ it 'raises an error' do
67
+ subject.split percentage: 10, url: :url, label: :label
68
+ expect { subject.split percentage: 10, url: :url, label: :label }.to raise_exception(Collection::DuplicateVersionException, 'supply unique labels')
69
+ end
70
+ end
71
+
72
+
73
+ context 'split supplied' do
74
+ it 'stores a split for the version' do
75
+ subject.split url: :url, label: :label, percentage: 50
76
+ expected = Collection::SplitRouteCollection.new(ForwardProxy.new(url: :url, id: :label,sitehub_cookie_name: :cookie_name) => 50)
77
+ expect(subject.endpoints).to eq(expected)
78
+ end
79
+ end
80
+
81
+ context 'routes defined' do
82
+ it 'throws and error' do
83
+ subject.route url: :url, label: :label
84
+ expect { subject.split(url: :url, label: :label, percentage: 50) }.to raise_error(ForwardProxyBuilder::InvalidDefinitionException)
85
+ end
86
+ end
87
+ end
88
+
89
+ describe 'route' do
90
+ it 'accepts a rule' do
91
+ subject.route url: :url, label: :current, rule: :rule
92
+ expected_route = ForwardProxy.new(url: :url, id: :current, rule: :rule, sitehub_cookie_name: :cookie_name)
93
+ expect(subject.endpoints).to eq({expected_route.id => expected_route})
94
+ end
95
+
96
+ context 'block supplied' do
97
+ context 'rule not supplied' do
98
+ it 'raise an error' do
99
+ expect { subject.route {} }.to raise_exception described_class::InvalidDefinitionException, 'rule must be specified when supplying a block'
100
+ end
101
+ end
102
+
103
+ it 'allows further splits or routes to be defined' do
104
+ rule = proc { true }
105
+ subject.route rule: rule do
106
+ route url: :url, label: :label1
107
+ end
108
+
109
+ subject.build
110
+
111
+ expected_route = ForwardProxy.new(url: :url, id: :label1, sitehub_cookie_name: :cookie_name)
112
+ expect(subject.resolve(env: {})).to eq(expected_route)
113
+ end
114
+
115
+ context 'invalid definitions inside block' do
116
+ it 'raises an error' do
117
+ rule = proc { true }
118
+ expect do
119
+ subject.route rule: rule do
120
+ split percentage: 20, url: :url, label: :label1
121
+ end
122
+ end.to raise_exception described_class::InvalidDefinitionException
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+
129
+ describe '#build' do
130
+
131
+ let(:rule) { proc {} }
132
+
133
+ context 'middleware not specified' do
134
+ it 'leaves it the proxies alone' do
135
+ subject.route url: :url, label: :current, rule: rule
136
+ proxy_before_build = subject.endpoints[:current]
137
+ subject.build
138
+ proxy_after_build = subject.endpoints[:current]
139
+ expect(proxy_after_build).to be(proxy_before_build)
140
+ expect(proxy_after_build.rule).to be(rule)
141
+ end
142
+
143
+ it 'does not extend the endpoint with Resolver' do
144
+ subject.route url: :url, label: :current, rule: rule
145
+ expect(subject.endpoints[:current]).to_not receive(:extend).with(Resolver)
146
+ subject.build
147
+
148
+ end
149
+ end
150
+
151
+ context 'middleware specified' do
152
+
153
+ before do
154
+ subject.use middleware
155
+ subject.route url: :url, label: :current, rule: rule
156
+ end
157
+
158
+ it 'wraps the forward proxies in the middleware' do
159
+ proxy_before_build = subject.endpoints[:current]
160
+
161
+ subject.build
162
+ proxy_after_build = subject.endpoints[:current]
163
+ expect(proxy_after_build).to be_a(middleware)
164
+ expect(proxy_after_build.app).to be(proxy_before_build)
165
+
166
+ end
167
+
168
+ it 'extends the middleware with Resolver' do
169
+ subject.build
170
+ expect(subject.endpoints[:current]).to be_a(Resolver)
171
+ end
172
+
173
+ it 'extends the middleware with Rules' do
174
+ subject.build
175
+ expect(subject.endpoints[:current]).to be_a(Rules)
176
+ end
177
+
178
+ context 'rule on route' do
179
+ it 'adds it to the rule to the middleware object' do
180
+ subject.build
181
+ expect(subject.endpoints[:current].rule).to eq(rule)
182
+ end
183
+ end
184
+
185
+ it 'wraps the default in the middleware' do
186
+ subject.default url: :url
187
+ default_proxy_before_build = subject.default_proxy
188
+
189
+ subject.build
190
+ default_proxy_after_build = subject.default_proxy
191
+ expect(default_proxy_after_build).to be_a(middleware)
192
+ expect(default_proxy_after_build.app).to be(default_proxy_before_build)
193
+ end
194
+ end
195
+
196
+ end
197
+ describe '#resolve' do
198
+
199
+ subject { described_class.new(mapped_path: '/') }
200
+
201
+ context 'routes defined' do
202
+ it 'returns that route' do
203
+ subject.route url: :url, label: :current
204
+ expect(subject.resolve(env: {})).to eq(subject.endpoints.values.first)
205
+ end
206
+
207
+ it 'passes the env to the when resolving the correct route' do
208
+ expect_any_instance_of(subject.routes.class).to receive(:resolve).with(env: :env).and_call_original
209
+ subject.resolve(env: :env)
210
+ end
211
+ end
212
+
213
+ context 'splits defined' do
214
+ it 'serves an entry from the routes' do
215
+ subject.split(percentage: 100, url: :url, label: :label)
216
+ expect(subject.endpoints).to receive(:resolve).and_return(:pick)
217
+ expect(subject.resolve(env: {})).to eq(:pick)
218
+ end
219
+
220
+ context 'splits not defined' do
221
+ it 'returns the default' do
222
+ subject.default url: :url
223
+ expect(subject.resolve(env: {})).to eq(subject.default_proxy)
224
+ end
225
+ end
226
+ end
227
+
228
+ context 'endpoints' do
229
+ context 'called with a collection' do
230
+ it 'sets endpoints to be that collection' do
231
+ subject.endpoints(:collection)
232
+ expect(subject.endpoints).to eq(:collection)
233
+ end
234
+ end
235
+
236
+ context 'already set with a different collection' do
237
+ it 'raise an error' do
238
+ subject.endpoints(:collection1)
239
+ expect { subject.endpoints(:collection2) }.to raise_exception described_class::InvalidDefinitionException
240
+ end
241
+ end
242
+
243
+ end
244
+
245
+
246
+ context 'version selected' do
247
+
248
+ context 'version applies to a route' do
249
+ before do
250
+ subject.split percentage: 50, url: :url1, label: :new
251
+ subject.split percentage: 50, url: :url2, label: :old
252
+ end
253
+
254
+ let(:application_version1) do
255
+ subject.endpoints.values.find do |pick|
256
+ pick.value.id == :new
257
+ end.value
258
+ end
259
+
260
+ context 'string supplied' do
261
+ it 'redirects to that version' do
262
+ expect(subject.resolve(id: :new.to_s, env: {})).to eq(application_version1)
263
+ end
264
+ end
265
+
266
+ context 'symbol supplied' do
267
+ it 'redirects to that version' do
268
+ expect(subject).to_not receive(:auto_resolve)
269
+ expect(subject.resolve(id: :new, env: {})).to eq(application_version1)
270
+ end
271
+ end
272
+ end
273
+
274
+ end
275
+ end
276
+
277
+ describe '#forward_proxy' do
278
+ subject do
279
+ described_class.new(mapped_path: '/path', sitehub_cookie_name: :expected_cookie_name)
280
+ end
281
+
282
+ it 'sets the sitehub_cookie_path' do
283
+ subject.sitehub_cookie_path :cookie_path
284
+ proxy = subject.forward_proxy(label: :label, url: :url)
285
+ expect(proxy.sitehub_cookie_path).to eq(:cookie_path)
286
+ end
287
+
288
+ it 'sets the sitehub_cookie_name' do
289
+ proxy = subject.forward_proxy(label: :label, url: :url)
290
+ expect(proxy.sitehub_cookie_name).to eq(:expected_cookie_name)
291
+ end
292
+ end
293
+ end
294
+
295
+ end