sitehub 0.4.1

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.
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