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.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.rspec +1 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +114 -0
- data/LICENSE +28 -0
- data/README.md +198 -0
- data/Rakefile +11 -0
- data/lib/sitehub.rb +9 -0
- data/lib/sitehub/builder.rb +82 -0
- data/lib/sitehub/collection.rb +35 -0
- data/lib/sitehub/collection/route_collection.rb +28 -0
- data/lib/sitehub/collection/split_route_collection.rb +50 -0
- data/lib/sitehub/collection/split_route_collection/split.rb +18 -0
- data/lib/sitehub/constants.rb +23 -0
- data/lib/sitehub/constants/http_header_keys.rb +25 -0
- data/lib/sitehub/constants/rack_http_header_keys.rb +17 -0
- data/lib/sitehub/cookie.rb +54 -0
- data/lib/sitehub/cookie/attribute.rb +22 -0
- data/lib/sitehub/cookie/flag.rb +22 -0
- data/lib/sitehub/cookie_rewriting.rb +35 -0
- data/lib/sitehub/forward_proxies.rb +50 -0
- data/lib/sitehub/forward_proxy.rb +67 -0
- data/lib/sitehub/forward_proxy_builder.rb +99 -0
- data/lib/sitehub/http_headers.rb +60 -0
- data/lib/sitehub/logging.rb +5 -0
- data/lib/sitehub/logging/access_logger.rb +74 -0
- data/lib/sitehub/logging/error_logger.rb +36 -0
- data/lib/sitehub/logging/log_entry.rb +14 -0
- data/lib/sitehub/logging/log_stash.rb +10 -0
- data/lib/sitehub/logging/log_wrapper.rb +23 -0
- data/lib/sitehub/middleware.rb +21 -0
- data/lib/sitehub/path_directive.rb +32 -0
- data/lib/sitehub/path_directives.rb +21 -0
- data/lib/sitehub/request_mapping.rb +43 -0
- data/lib/sitehub/resolver.rb +11 -0
- data/lib/sitehub/reverse_proxy.rb +53 -0
- data/lib/sitehub/rules.rb +13 -0
- data/lib/sitehub/string_sanitiser.rb +7 -0
- data/lib/sitehub/transaction_id.rb +16 -0
- data/lib/sitehub/version.rb +3 -0
- data/mem_usage.txt +1584 -0
- data/sitehub.gemspec +43 -0
- data/spec/basket_spec.rb +30 -0
- data/spec/sitehub/builder_spec.rb +203 -0
- data/spec/sitehub/collection/route_collection_spec.rb +91 -0
- data/spec/sitehub/collection/split_route_collection_spec.rb +111 -0
- data/spec/sitehub/collection_spec.rb +40 -0
- data/spec/sitehub/cookie/attribute_spec.rb +37 -0
- data/spec/sitehub/cookie/flag_spec.rb +27 -0
- data/spec/sitehub/cookie_rewriting_spec.rb +67 -0
- data/spec/sitehub/cookie_spec.rb +61 -0
- data/spec/sitehub/error_handling_spec.rb +21 -0
- data/spec/sitehub/forward_proxies_spec.rb +99 -0
- data/spec/sitehub/forward_proxy_builder_spec.rb +295 -0
- data/spec/sitehub/forward_proxy_spec.rb +138 -0
- data/spec/sitehub/http_headers_spec.rb +71 -0
- data/spec/sitehub/integration_spec.rb +21 -0
- data/spec/sitehub/logging/access_logger_spec.rb +127 -0
- data/spec/sitehub/logging/error_logger_spec.rb +80 -0
- data/spec/sitehub/logging/log_entry_spec.rb +34 -0
- data/spec/sitehub/logging/log_stash_spec.rb +21 -0
- data/spec/sitehub/logging/log_wrapper_spec.rb +33 -0
- data/spec/sitehub/middleware_spec.rb +69 -0
- data/spec/sitehub/path_directive_spec.rb +50 -0
- data/spec/sitehub/path_directives_spec.rb +45 -0
- data/spec/sitehub/request_mapping_spec.rb +71 -0
- data/spec/sitehub/resolver_spec.rb +15 -0
- data/spec/sitehub/reverse_proxy_spec.rb +105 -0
- data/spec/sitehub/transaction_id_spec.rb +28 -0
- data/spec/sitehub_spec.rb +19 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/patch/rack/response.rb +25 -0
- data/spec/support/shared_contexts/async_context.rb +69 -0
- data/spec/support/shared_contexts/middleware_context.rb +51 -0
- data/spec/support/shared_contexts/rack_test_context.rb +12 -0
- data/spec/support/shared_contexts/sitehub_context.rb +25 -0
- data/spec/support/silent_warnings.rb +5 -0
- 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
|