sitehub 0.5.0.alpha7 → 0.5.0.alpha8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: da2a5c038108c8668145aa88a4c0a828b6b922a9
4
- data.tar.gz: 54aa60369801ce0c17e5cf63534476b89982ed7b
3
+ metadata.gz: 2389132d290dc9bcff1fca5999cc75802e31af1a
4
+ data.tar.gz: dfca66250eac7e57b5ec53002d2c662ede650688
5
5
  SHA512:
6
- metadata.gz: 41ecea0098d4388ca85887cf8c5bdbab22f29d10f05c16559378aa89a893fcc6149e66eb538ee8b54ce0635ca2e168f4e23b03004e94d5f0765b5a991028fdd1
7
- data.tar.gz: 1e0da0d04f15a22558b26140884f4f5f7ba20f13515d35d8ee7b67bf60836fd6698ec97110ef53c683043761a336f12ca19824ecafd5ef8cfd0eec6396596d87
6
+ metadata.gz: 27a39c2ac4aea35dcf4ae1b73a7ad8fd77c23f3f0fc1e22c4829a0052eec38cec2d1d4eaac32e84906e442297855eefa7afce5e6eccf1a33605a04ca84fcd001
7
+ data.tar.gz: fb3e3f8a9d9eb65ef2ad3b714bec96a716cbaf5d8b0fde70dd5e5418a887901e7f07403f4e63249e4f736ce4e10a7f723afe90d21b2312803e0c22de771c4341
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sitehub (0.5.0.alpha7)
4
+ sitehub (0.5.0.alpha8)
5
5
  activesupport
6
6
  em-http-request
7
7
  em-synchrony
@@ -7,8 +7,15 @@ class SiteHub
7
7
  include Middleware
8
8
  extend GetterSetterMethods
9
9
 
10
- attr_reader :core
11
- getter_setters :access_logger, :error_logger, :config_server
10
+ DEFAULT_CACHING_OPTIONS = { expires_in: 30 }.freeze
11
+
12
+ attr_reader :core, :config_server_url, :config_server_caching_options
13
+ getter_setters :access_logger, :error_logger
14
+
15
+ def config_server(url, caching_options: DEFAULT_CACHING_OPTIONS)
16
+ @config_server_url = url
17
+ @config_server_caching_options = caching_options
18
+ end
12
19
 
13
20
  def force_ssl(except: [])
14
21
  @force_ssl = true
@@ -22,7 +29,7 @@ class SiteHub
22
29
 
23
30
  def build
24
31
  add_default_middleware
25
- use ConfigLoader, config_server if config_server
32
+ use ConfigLoader, config_server_url, caching_options: config_server_caching_options if config_server_url
26
33
  apply_middleware(core.build)
27
34
  end
28
35
 
@@ -15,7 +15,7 @@ require_relative 'downstream_client'
15
15
 
16
16
  class SiteHub
17
17
  class CandidateRoutes
18
- class InvalidDefinitionException < Exception
18
+ class InvalidDefinitionError < StandardError
19
19
  end
20
20
 
21
21
  ROUTES_WITH_SPLITS_MSG = 'you cant register routes and splits at the same level'.freeze
@@ -37,11 +37,11 @@ class SiteHub
37
37
  child_label = id.child_label(label)
38
38
 
39
39
  route = if block
40
- raise InvalidDefinitionException, candidate_definition_msg unless percentage || rule
40
+ raise InvalidDefinitionError, candidate_definition_msg unless percentage || rule
41
41
  warn(IGNORING_URL_MSG) if url
42
42
  new(rule: rule, id: child_label, &block).build
43
43
  else
44
- raise InvalidDefinitionException, URL_REQUIRED_MSG unless url
44
+ raise InvalidDefinitionError, URL_REQUIRED_MSG unless url
45
45
  forward_proxy(url: url, label: child_label, rule: rule)
46
46
  end
47
47
 
@@ -56,7 +56,7 @@ class SiteHub
56
56
  def candidates(collection = nil)
57
57
  return @endpoints ||= Collection::RouteCollection.new unless collection
58
58
 
59
- raise InvalidDefinitionException, ROUTES_WITH_SPLITS_MSG if @endpoints && !@endpoints.equal?(collection)
59
+ raise InvalidDefinitionError, ROUTES_WITH_SPLITS_MSG if @endpoints && !@endpoints.equal?(collection)
60
60
  @endpoints = collection
61
61
  end
62
62
 
@@ -96,7 +96,7 @@ class SiteHub
96
96
  return unless block_given?
97
97
 
98
98
  instance_eval(&block)
99
- raise InvalidDefinitionException unless valid?
99
+ raise InvalidDefinitionError unless valid?
100
100
  end
101
101
 
102
102
  def method_missing(method, *args, &block)
@@ -1,6 +1,6 @@
1
1
  class SiteHub
2
2
  class Collection < Hash
3
- class DuplicateVersionException < Exception
3
+ class DuplicateVersionError < StandardError
4
4
  end
5
5
 
6
6
  module ClassMethods
@@ -16,7 +16,7 @@ class SiteHub
16
16
  alias_method :add_backup, :add
17
17
 
18
18
  send(:define_method, :add) do |id, value, *args|
19
- raise DuplicateVersionException, UNIQUE_LABELS_MSG if key?(id)
19
+ raise DuplicateVersionError, UNIQUE_LABELS_MSG if key?(id)
20
20
  add_backup id, value, *args
21
21
  end
22
22
  end
@@ -1,4 +1,7 @@
1
1
  require_relative '../collection'
2
+ # rubocop:disable Lint/UnusedMethodArgument -
3
+ # this is because #resolve is supporting a duck typed interface and needs the env parameter
4
+
2
5
  class SiteHub
3
6
  class Collection < Hash
4
7
  class RouteCollection < Collection
@@ -3,7 +3,7 @@ require_relative 'split_route_collection/split'
3
3
  class SiteHub
4
4
  class Collection < Hash
5
5
  class SplitRouteCollection < Collection
6
- class InvalidSplitException < Exception
6
+ class InvalidSplitException < StandardError
7
7
  end
8
8
 
9
9
  FIXNUM_ERR_MSG = 'splits must be a Fixnum'.freeze
@@ -0,0 +1,32 @@
1
+ class SiteHub
2
+ class ConfigServer
3
+ BAD_JSON_MSG = 'Illegal JSON returned from config server: %s'.freeze
4
+ UNABLE_TO_CONTACT_SERVER_MSG = 'Unabled to contact server: %s'.freeze
5
+ NON_200_RESPONSE_MSG = 'Config server did not respond with a 200, got %s'.freeze
6
+
7
+ class Error < StandardError
8
+ end
9
+
10
+ attr_reader :server_url, :http_client
11
+ def initialize(url)
12
+ @server_url = url
13
+ @http_client = Faraday.new(ssl: { verify: false }) do |con|
14
+ con.adapter :em_synchrony
15
+ end
16
+ end
17
+
18
+ def get
19
+ response = http_client.get(server_url)
20
+ raise Error, NON_200_RESPONSE_MSG % response.status unless response.status == 200
21
+ parse_response(response.body)
22
+ rescue Faraday::Error => e
23
+ raise Error, UNABLE_TO_CONTACT_SERVER_MSG % e.message
24
+ end
25
+
26
+ def parse_response(response_body)
27
+ JSON(response_body, symbolize_names: true)
28
+ rescue JSON::ParserError
29
+ raise Error, BAD_JSON_MSG % response_body
30
+ end
31
+ end
32
+ end
@@ -3,10 +3,10 @@ require 'sitehub/candidate_routes'
3
3
  require 'forwardable'
4
4
 
5
5
  class SiteHub
6
- class InvalidProxyDefinitionException < Exception
6
+ class InvalidProxyDefinitionException < StandardError
7
7
  end
8
8
 
9
- class ConfigError < Exception
9
+ class ConfigError < StandardError
10
10
  end
11
11
 
12
12
  class Core
@@ -9,6 +9,10 @@ require 'em-http'
9
9
  class SiteHub
10
10
  module Middleware
11
11
  class CandidateRouteMappings < Hash
12
+ INVALID_PATH_MATCHER = 'Matcher for path (%s) was not a valid regexp: %s'.freeze
13
+ class InvalidPathMatcherError < StandardError
14
+ end
15
+
12
16
  NIL_ROUTE = NilRoute.new
13
17
 
14
18
  include Equality
@@ -39,6 +43,8 @@ class SiteHub
39
43
  return
40
44
  end
41
45
 
46
+ mapped_path = string_to_regexp(mapped_path) if string_containing_regexp?(mapped_path)
47
+
42
48
  self[mapped_path] = CandidateRoutes.new(sitehub_cookie_name: sitehub_cookie_name,
43
49
  mapped_path: mapped_path,
44
50
  &block).tap do |builder|
@@ -60,6 +66,20 @@ class SiteHub
60
66
  end
61
67
  end
62
68
  end
69
+
70
+ def string_containing_regexp?(obj)
71
+ return false unless obj.is_a?(String)
72
+ obj.start_with?('%r{') && obj.end_with?('}')
73
+ end
74
+
75
+ private
76
+
77
+ def string_to_regexp(mapped_path)
78
+ regexp_string = mapped_path.to_s.sub(/^%r{/, '').sub(/}$/, '')
79
+ Regexp.compile(regexp_string)
80
+ rescue RegexpError => e
81
+ raise InvalidPathMatcherError, format(INVALID_PATH_MATCHER, regexp_string, e.message)
82
+ end
63
83
  end
64
84
  end
65
85
  end
@@ -1,36 +1,33 @@
1
1
  require 'active_support'
2
+ require 'sitehub/config_server'
2
3
  class SiteHub
3
- class ConfigServer
4
- attr_reader :server_url, :http_client
5
- def initialize(url)
6
- @server_url = url
7
- @http_client = Faraday.new(ssl: { verify: false }) do |con|
8
- con.adapter :em_synchrony
9
- end
10
- end
11
-
12
- def get
13
- JSON(http_client.get(server_url).body, symbolize_names: true)
14
- end
15
- end
16
-
17
4
  module Middleware
18
5
  class ConfigLoader
19
- attr_reader :config_server, :app, :cache
6
+ attr_reader :config_server, :app, :cache, :caching_options
20
7
 
21
- def initialize(_app, config_server_url)
8
+ def initialize(_app, config_server_url, caching_options:)
22
9
  @config_server = ConfigServer.new(config_server_url)
23
10
  @cache = ActiveSupport::Cache::MemoryStore.new(size: 1.megabytes)
11
+ @caching_options = caching_options
24
12
  end
25
13
 
26
14
  def call(env)
27
- load_config
15
+ begin
16
+ load_config
17
+ rescue ConfigServer::Error => e
18
+ if @app
19
+ env[ERRORS] << e.message
20
+ else
21
+ raise e unless @app
22
+ end
23
+ end
24
+
28
25
  @app.call env
29
26
  end
30
27
 
31
28
  # TODO: handle errors connecting to the config server
32
29
  def load_config
33
- config = cache.fetch(:sitehub_config, expires_in: 30) do
30
+ config = cache.fetch(:sitehub_config, caching_options) do
34
31
  config_server.get
35
32
  end
36
33
 
@@ -1,3 +1,3 @@
1
1
  class SiteHub
2
- VERSION = '0.5.0.alpha7'.freeze
2
+ VERSION = '0.5.0.alpha8'.freeze
3
3
  end
@@ -1,4 +1,5 @@
1
1
  require 'cgi'
2
+
2
3
  describe 'access_logs' do
3
4
  let(:downstream_url) { 'http://localhost:12345/experiment1' }
4
5
 
@@ -30,10 +31,13 @@ describe 'access_logs' do
30
31
  let(:query_string) { '' }
31
32
  let(:request_url) { "/endpoint#{query_string.empty? ? '' : "?#{query_string}"}" }
32
33
 
33
- subject do
34
+ before do
34
35
  query_string_hash = CGI.parse(query_string).collect { |key, value| [key, value.first] }.to_h
35
36
  stub_request(:get, downstream_url).with(query: query_string_hash)
36
37
  get(request_url)
38
+ end
39
+
40
+ subject do
37
41
  access_logger.string
38
42
  end
39
43
 
@@ -41,20 +45,28 @@ describe 'access_logs' do
41
45
  context 'present' do
42
46
  let(:query_string) { 'key=value' }
43
47
  it 'logs it' do
44
- expect(subject).to include(query_string)
48
+ expect(subject).to include("GET #{request_url} => #{downstream_url}")
45
49
  end
46
50
  end
47
51
 
48
52
  context 'not present' do
49
53
  let(:query_string) { '' }
50
54
  it 'is not logged' do
55
+ expect(subject).to match(/"GET\s#{request_url}\s=>\s#{downstream_url}\s/)
51
56
  expect(subject).to include(query_string)
52
57
  end
53
58
  end
54
59
  end
55
60
 
61
+ module WebMock
62
+ def self.last_request
63
+ WebMock::RequestRegistry.instance.requested_signatures.hash.keys.first
64
+ end
65
+ end
66
+
56
67
  it 'logs the transaction id' do
57
- expect(subject).to match(/transaction_id:.*?\s/)
68
+ expected_id = WebMock.last_request.headers['Sitehub-Transaction-Id']
69
+ expect(subject).to match(/transaction_id:#{expected_id}/)
58
70
  end
59
71
 
60
72
  it 'logs the response status' do
@@ -69,7 +81,6 @@ describe 'access_logs' do
69
81
  processing_time_matcher = '\d{1}\.\d{4}'
70
82
  transaction_id_matcher = '[a-z\d]+'
71
83
  expected_response_status = 200
72
- puts subject
73
84
  expect(subject).to match(/transaction_id:#{transaction_id_matcher}:\s"GET\s#{request_url}\s=>\s#{downstream_url}\s"\s#{expected_response_status}\s-\s#{processing_time_matcher}/)
74
85
  end
75
86
  end
@@ -0,0 +1,77 @@
1
+ require 'stringio'
2
+ describe 'error handling' do
3
+ context 'using config server' do
4
+ CONFIG_SERVER_URL = 'http://the.config.server'.freeze
5
+ DOWNSTREAM_URL = 'http://downstream.url'.freeze
6
+ ERROR_LOGGER = StringIO.new
7
+ ACCESS_LOGGER = StringIO.new
8
+
9
+ include_context :sitehub_json
10
+
11
+ let(:config) do
12
+ { proxies: [
13
+ {
14
+ path: '/',
15
+ default: DOWNSTREAM_URL
16
+ }
17
+ ] }
18
+ end
19
+
20
+ let(:downstream_response) { 'downstream response' }
21
+
22
+ before do
23
+ stub_request(:get, DOWNSTREAM_URL).and_return(body: downstream_response)
24
+ end
25
+ let(:app) do
26
+ sitehub = SiteHub.build do
27
+ config_server(CONFIG_SERVER_URL, caching_options: { force: true })
28
+ error_logger ERROR_LOGGER
29
+ access_logger ACCESS_LOGGER
30
+ end
31
+ Async::Middleware.new(sitehub)
32
+ end
33
+
34
+ it 'reads the config for routing' do
35
+ stub_request(:get, CONFIG_SERVER_URL).and_return(body: config.to_json)
36
+ get('/')
37
+ expect(app.last_response.body).to eq([downstream_response])
38
+ end
39
+
40
+ context 'config server is broken' do
41
+ let(:bad_status) { 500 }
42
+ context 'config has not been loaded' do
43
+ it 'returns an error' do
44
+ stub_request(:get, CONFIG_SERVER_URL).and_return(status: 500)
45
+ get('/')
46
+ expect(app.last_response.status).to eq(500)
47
+ end
48
+
49
+ it 'logs an error' do
50
+ bad_status = 500
51
+ stub_request(:get, CONFIG_SERVER_URL).and_return(status: bad_status)
52
+ get('/')
53
+ expect(ERROR_LOGGER.string).to include(SiteHub::ConfigServer::NON_200_RESPONSE_MSG % bad_status)
54
+ end
55
+ end
56
+
57
+ context 'config has been correctly loaded once' do
58
+ before do
59
+ stub_request(:get, CONFIG_SERVER_URL).and_return(body: config.to_json)
60
+ get('/')
61
+
62
+ stub_request(:get, CONFIG_SERVER_URL).and_return(status: bad_status)
63
+ end
64
+
65
+ it 'uses the old config' do
66
+ get('/')
67
+ expect(app.last_response.body).to eq([downstream_response])
68
+ end
69
+
70
+ it 'logs the failure to read config' do
71
+ get('/')
72
+ expect(ERROR_LOGGER.string).to include(SiteHub::ConfigServer::NON_200_RESPONSE_MSG % bad_status)
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -163,6 +163,27 @@ class SiteHub
163
163
 
164
164
  expect(middleware_stack).to eq(expected_middleware)
165
165
  end
166
+
167
+ it 'defaults caching policy' do
168
+ subject.config_server(:server_url)
169
+
170
+ sitehub = subject.build
171
+
172
+ config_loader = find_middleware(sitehub, Middleware::ConfigLoader)
173
+ expect(config_loader.caching_options).to eq(described_class::DEFAULT_CACHING_OPTIONS)
174
+ end
175
+
176
+ context 'caching options specified' do
177
+ it 'uses them' do
178
+ caching_options = { force: true }
179
+ subject.config_server(:server_url, caching_options: caching_options)
180
+
181
+ sitehub = subject.build
182
+
183
+ config_loader = find_middleware(sitehub, Middleware::ConfigLoader)
184
+ expect(config_loader.caching_options).to eq(caching_options)
185
+ end
186
+ end
166
187
  end
167
188
 
168
189
  it 'adds them in the right order' do
@@ -148,7 +148,7 @@ class SiteHub
148
148
  it 'raises an error' do
149
149
  subject.candidates(Collection::SplitRouteCollection.new)
150
150
  expect { subject.candidates(Collection::RouteCollection.new) }
151
- .to raise_error(CandidateRoutes::InvalidDefinitionException)
151
+ .to raise_error(CandidateRoutes::InvalidDefinitionError)
152
152
  end
153
153
  end
154
154
  end
@@ -301,7 +301,7 @@ class SiteHub
301
301
  subject.candidates(Collection::SplitRouteCollection.new)
302
302
  expected_message = described_class::PERCENTAGE_NOT_SPECIFIED_MSG
303
303
  expect { subject.add(label: :label) {} }
304
- .to raise_exception described_class::InvalidDefinitionException, expected_message
304
+ .to raise_exception described_class::InvalidDefinitionError, expected_message
305
305
  end
306
306
  end
307
307
 
@@ -310,7 +310,7 @@ class SiteHub
310
310
  subject.candidates(Collection::RouteCollection.new)
311
311
  expected_message = described_class::RULE_NOT_SPECIFIED_MSG
312
312
  expect { subject.add(label: :label) {} }
313
- .to raise_exception described_class::InvalidDefinitionException, expected_message
313
+ .to raise_exception described_class::InvalidDefinitionError, expected_message
314
314
  end
315
315
  end
316
316
  end
@@ -327,8 +327,11 @@ class SiteHub
327
327
  rule = proc { true }
328
328
  subject.add(rule: rule, label: :label, &block)
329
329
 
330
- expected_endpoints = described_class.new(id: :label, sitehub_cookie_name: :cookie_name,
331
- rule: rule, mapped_path: subject.mapped_path, &block).tap do |builder|
330
+ expected_endpoints = described_class.new(id: :label,
331
+ sitehub_cookie_name: :cookie_name,
332
+ rule: rule,
333
+ mapped_path: subject.mapped_path,
334
+ &block).tap do |builder|
332
335
  builder.sitehub_cookie_name subject.sitehub_cookie_name
333
336
  end.build
334
337
 
@@ -342,7 +345,7 @@ class SiteHub
342
345
  subject.add rule: rule, label: :label do
343
346
  split percentage: 20, url: :url, label: :label1
344
347
  end
345
- end.to raise_exception described_class::InvalidDefinitionException
348
+ end.to raise_exception described_class::InvalidDefinitionError
346
349
  end
347
350
  end
348
351
  end
@@ -464,7 +467,7 @@ class SiteHub
464
467
  context 'already set with a different collection' do
465
468
  it 'raise an error' do
466
469
  subject.candidates(:collection1)
467
- expect { subject.candidates(:collection2) }.to raise_exception described_class::InvalidDefinitionException
470
+ expect { subject.candidates(:collection2) }.to raise_exception described_class::InvalidDefinitionError
468
471
  end
469
472
  end
470
473
  end
@@ -32,7 +32,7 @@ class SiteHub
32
32
  subject.add(duplicate.id, duplicate)
33
33
  expected_message = described_class::ClassMethods::UNIQUE_LABELS_MSG
34
34
  expect { subject.add(duplicate.id, duplicate) }
35
- .to raise_exception described_class::DuplicateVersionException, expected_message
35
+ .to raise_exception described_class::DuplicateVersionError, expected_message
36
36
  end
37
37
  end
38
38
  end
@@ -0,0 +1,48 @@
1
+ require 'sitehub/config_server'
2
+
3
+ class SiteHub
4
+ describe ConfigServer do
5
+ include_context :sitehub_json
6
+
7
+ let(:server_url) { 'http://www.server.url' }
8
+ let(:config) { { config: 'value' } }
9
+
10
+ subject do
11
+ described_class.new(server_url)
12
+ end
13
+
14
+ describe '#get' do
15
+ context 'non 200 returned' do
16
+ it 'raises an error' do
17
+ bad_response_code = 500
18
+ stub_request(:get, server_url).to_return(body: config.to_json, status: bad_response_code)
19
+ expected_message = described_class::NON_200_RESPONSE_MSG % bad_response_code
20
+ expect { subject.get }.to raise_error(described_class::Error, expected_message)
21
+ end
22
+ end
23
+
24
+ context 'exception thrown in client' do
25
+ it 'raises an error' do
26
+ error_msg = 'error from library'
27
+ expect_any_instance_of(Faraday::Connection).to receive(:get).and_raise(Faraday::ConnectionFailed, error_msg)
28
+ expected_message = described_class::UNABLE_TO_CONTACT_SERVER_MSG % error_msg
29
+ expect { subject.get }.to raise_error(described_class::Error, expected_message)
30
+ end
31
+ end
32
+
33
+ context 'malformed json returned' do
34
+ it 'returns an error' do
35
+ bad_json = 'bad json'
36
+ stub_request(:get, server_url).to_return(body: bad_json)
37
+ expected_message = described_class::BAD_JSON_MSG % bad_json
38
+ expect { subject.get }.to raise_error(described_class::Error, expected_message)
39
+ end
40
+ end
41
+
42
+ it 'returns config as a hash' do
43
+ stub_request(:get, server_url).to_return(body: config.to_json)
44
+ expect(subject.get).to eq(config)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -45,6 +45,15 @@ class SiteHub
45
45
  end
46
46
  end
47
47
 
48
+ context 'query string' do
49
+ let(:params) { { 'key' => 'value' } }
50
+ let(:env) { env_for(path: mapped_path, params_or_body: params) }
51
+ it 'preserves the query string when forwarding the request' do
52
+ stub_request(http_method, current_version_url).with(query: params)
53
+ subject.call(request)
54
+ end
55
+ end
56
+
48
57
  it 'preserves the headers when forwarding request' do
49
58
  http_headers['HTTP_HEADER'] = 'value'
50
59
  subject.call(request)
@@ -21,10 +21,10 @@ class SiteHub
21
21
  end
22
22
 
23
23
  before do
24
- subject.init
24
+ # subject.init
25
25
  end
26
26
 
27
- describe '#add_proxy' do
27
+ describe '#add_route' do
28
28
  def route(app, id:)
29
29
  Route.new(app,
30
30
  id: id,
@@ -41,6 +41,51 @@ class SiteHub
41
41
  end
42
42
  end
43
43
 
44
+ context 'mapped_path' do
45
+ let(:expected_route) do
46
+ proxy = ForwardProxy.new(mapped_path: expected_mapping, mapped_url: base_url)
47
+ route(proxy, id: :current)
48
+ end
49
+
50
+ context 'mapped_path is a string' do
51
+ let(:mapped_path) { 'string' }
52
+ let(:expected_mapping) { mapped_path }
53
+ it 'stores the route against that strubg' do
54
+ expect(subject[mapped_path][:current]).to eq(expected_route)
55
+ end
56
+ end
57
+
58
+ context 'mapped_path is a valid regex' do
59
+ let(:mapped_path) { /regular_expression/ }
60
+ let(:expected_mapping) { mapped_path }
61
+ it 'stores the route against that regexp' do
62
+ expect(subject[mapped_path][:current]).to eq(expected_route)
63
+ end
64
+ end
65
+
66
+ context 'is a string containing a valid regexp' do
67
+ let(:expected_mapping) { /regexp/ }
68
+ let(:mapped_path) { '%r{regexp}' }
69
+ it 'stores the route against the string coverted to a regexp' do
70
+ expect(subject[expected_mapping][:current]).to eq(expected_route)
71
+ end
72
+ end
73
+
74
+ context 'is a string containing malformed regexp' do
75
+ let(:mapped_path) { '%r{*}' }
76
+
77
+ it 'raises an error' do
78
+ expected_message = begin
79
+ Regexp.compile('*')
80
+ rescue RegexpError => e
81
+ format(described_class::INVALID_PATH_MATCHER, '*', e.message)
82
+ end
83
+
84
+ expect { subject }.to raise_error(described_class::InvalidPathMatcherError, expected_message)
85
+ end
86
+ end
87
+ end
88
+
44
89
  context 'url specified' do
45
90
  let(:expected_route) do
46
91
  proxy = ForwardProxy.new(mapped_path: mapped_path, mapped_url: :url)
@@ -145,6 +190,28 @@ class SiteHub
145
190
  end
146
191
  end
147
192
  end
193
+
194
+ describe '#string_containing_regexp?' do
195
+ context 'parameter is not a string' do
196
+ it 'it returns false' do
197
+ expect(subject.string_containing_regexp?(//)).to eq(false)
198
+ end
199
+ end
200
+
201
+ context 'parameter is a string' do
202
+ context 'contains a regexp' do
203
+ it 'it returns true' do
204
+ expect(subject.string_containing_regexp?('%r{}')).to be(true)
205
+ end
206
+ end
207
+
208
+ context 'does not contain a regexp' do
209
+ it 'returns false' do
210
+ expect(subject.string_containing_regexp?('')).to eq(false)
211
+ end
212
+ end
213
+ end
214
+ end
148
215
  end
149
216
  end
150
217
  end
@@ -26,24 +26,40 @@ class SiteHub
26
26
  stub_request(:get, server_url).to_return(body: config.to_json)
27
27
  end
28
28
 
29
+ let(:no_caching) { { force: true } }
29
30
  subject do
30
- described_class.new(:app, server_url)
31
+ described_class.new(:app, server_url, caching_options: no_caching)
31
32
  end
32
33
 
33
34
  describe '#load_config' do
34
- it 'loads config' do
35
- expect(subject.app).to be_nil
36
- subject.load_config
35
+ subject do
36
+ caching_enabled = { expires_in: 30 }
37
+ described_class.new(:app, server_url, caching_options: caching_enabled)
38
+ end
37
39
 
38
- expected_core = Core.new do
40
+ let(:expected_core) do
41
+ Core.new do
39
42
  sitehub_cookie_name 'sitehub.recorded_route'
40
43
  proxy '/route_1' do
41
44
  route label: :label_1, url: 'http://lvl-up.uk/'
42
45
  end
43
46
  end.build
47
+ end
48
+
49
+ it 'loads config' do
50
+ expect(subject.app).to be_nil
51
+ subject.load_config
44
52
 
45
53
  expect(subject.app).to eq(expected_core)
46
54
  end
55
+
56
+ it 'loads it from cache' do
57
+ different_config = { proxies: [] }
58
+ subject.load_config
59
+ stub_request(:get, server_url).to_return(body: different_config.to_json)
60
+ subject.load_config
61
+ expect(subject.app).to eq(expected_core)
62
+ end
47
63
  end
48
64
 
49
65
  describe '#call' do
@@ -58,6 +74,61 @@ class SiteHub
58
74
 
59
75
  expect(subject.call(:env)).to eq(response)
60
76
  end
77
+
78
+ context 'config not retrievable' do
79
+ let(:config_server) do
80
+ instance_double(ConfigServer).tap do |config_server|
81
+ expect(ConfigServer).to receive(:new).and_return(config_server)
82
+ end
83
+ end
84
+
85
+ let(:env) { { ERRORS => [] } }
86
+
87
+ context 'first call ever received' do
88
+ it 'raises an error' do
89
+ expected_message = 'error message'
90
+ expect(config_server).to receive(:get).and_raise(ConfigServer::Error, expected_message)
91
+ expect { subject.call(env) }.to raise_error(ConfigServer::Error, expected_message)
92
+ end
93
+
94
+ it 'does not write anything to errors' do
95
+ expect(config_server).to receive(:get).and_raise(ConfigServer::Error)
96
+
97
+ expect { subject.call(env) }.to raise_error(ConfigServer::Error)
98
+ expect(env[ERRORS]).to be_empty
99
+ end
100
+ end
101
+
102
+ context 'config previously loaded' do
103
+ subject do
104
+ described_class.new(:app, server_url, caching_options: no_caching)
105
+ end
106
+
107
+ let(:response) { [200, {}, []] }
108
+ before do
109
+ app = proc do |_env|
110
+ response
111
+ end
112
+
113
+ expect(config_server).to receive(:get).and_return(config)
114
+ expect(Core).to receive(:from_hash).with(config).and_return(double(build: app))
115
+ subject.call(env)
116
+ end
117
+
118
+ it 'retains the original config' do
119
+ expect(config_server).to receive(:get).and_raise(ConfigServer::Error)
120
+ expect(subject.call(env)).to eq(response)
121
+ end
122
+
123
+ it 'writes an error to the error log' do
124
+ expected_error_message = 'error message'
125
+ expect(config_server).to receive(:get).and_raise(ConfigServer::Error, expected_error_message)
126
+
127
+ subject.call(env)
128
+ expect(env[ERRORS]).to include(expected_error_message)
129
+ end
130
+ end
131
+ end
61
132
  end
62
133
  end
63
134
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sitehub
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0.alpha7
4
+ version: 0.5.0.alpha8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ladtech
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-03 00:00:00.000000000 Z
11
+ date: 2016-09-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -346,6 +346,7 @@ files:
346
346
  - lib/sitehub/collection/split_route_collection.rb
347
347
  - lib/sitehub/collection/split_route_collection/split.rb
348
348
  - lib/sitehub/collection_methods.rb
349
+ - lib/sitehub/config_server.rb
349
350
  - lib/sitehub/constants.rb
350
351
  - lib/sitehub/constants/http_header_keys.rb
351
352
  - lib/sitehub/constants/rack_http_header_keys.rb
@@ -390,6 +391,7 @@ files:
390
391
  - sitehub.gemspec
391
392
  - spec/equality_spec.rb
392
393
  - spec/integration/access_logs_spec.rb
394
+ - spec/integration/error_handling_spec.rb
393
395
  - spec/integration/middleware_spec.rb
394
396
  - spec/integration/version_affinity_spec.rb
395
397
  - spec/sitehub/builder_spec.rb
@@ -398,6 +400,7 @@ files:
398
400
  - spec/sitehub/collection/split_route_collection/split_spec.rb
399
401
  - spec/sitehub/collection/split_route_collection_spec.rb
400
402
  - spec/sitehub/collection_spec.rb
403
+ - spec/sitehub/config_server_spec.rb
401
404
  - spec/sitehub/cookie/attribute_spec.rb
402
405
  - spec/sitehub/cookie/flag_spec.rb
403
406
  - spec/sitehub/cookie/rewriting_spec.rb