sitehub 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,138 @@
|
|
1
|
+
require 'sitehub/forward_proxy'
|
2
|
+
|
3
|
+
class SiteHub
|
4
|
+
describe ForwardProxy do
|
5
|
+
|
6
|
+
let(:current_version_url) { 'http://does.not.exist.com' }
|
7
|
+
let(:mapped_path) { '/path' }
|
8
|
+
|
9
|
+
subject do
|
10
|
+
described_class.new(id: :id, url: current_version_url, mapped_path: mapped_path,sitehub_cookie_name: :cookie_name)
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:app) do
|
14
|
+
subject
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'includes Resolver' do
|
18
|
+
expect(subject).to be_a(Resolver)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'includes Rules' do
|
22
|
+
expect(subject).to be_a(Rules)
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#call' do
|
26
|
+
before do
|
27
|
+
WebMock.enable!
|
28
|
+
stub_request(:get, current_version_url).to_return(:body => 'body')
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'recorded routes cookie' do
|
32
|
+
it 'drops a cookie using the name of the sitehub_cookie_name containing the id' do
|
33
|
+
get(mapped_path, {})
|
34
|
+
expect(last_response.cookies[:cookie_name.to_s]).to eq(value: :id.to_s, path: subject.mapped_path)
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'recorded_routes_cookie_path not set' do
|
38
|
+
it 'sets the path to be the request path' do
|
39
|
+
get(mapped_path, {})
|
40
|
+
expect(last_response.cookies[:cookie_name.to_s][:path]).to eq(mapped_path)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'recorded_routes_cookie_path set' do
|
45
|
+
|
46
|
+
let(:expected_path){'/expected_path'}
|
47
|
+
|
48
|
+
subject do
|
49
|
+
described_class.new(id: :id,
|
50
|
+
url: current_version_url,
|
51
|
+
mapped_path: mapped_path,
|
52
|
+
sitehub_cookie_path: expected_path,
|
53
|
+
sitehub_cookie_name: :cookie_name)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'is set as the path' do
|
57
|
+
get(mapped_path, {})
|
58
|
+
expect(last_response.cookies[:cookie_name.to_s][:path]).to eq(expected_path)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
it 'passes request mapping information in to the environment hash' do
|
66
|
+
get(mapped_path, {})
|
67
|
+
expect(last_request.env[REQUEST_MAPPING]).to eq(RequestMapping.new(source_url: "http://example.org#{mapped_path}", mapped_url: current_version_url, mapped_path: mapped_path))
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'downstream call' do
|
71
|
+
context 'it fails' do
|
72
|
+
before do
|
73
|
+
WebMock.disable!
|
74
|
+
end
|
75
|
+
it 'adds an error to be logged' do
|
76
|
+
env = {ERRORS.to_s => []}
|
77
|
+
get(mapped_path, {}, env)
|
78
|
+
expect(last_request.env[ERRORS]).to eq(["unable to resolve server address"])
|
79
|
+
end
|
80
|
+
|
81
|
+
describe 'parameters to callback' do
|
82
|
+
it 'calls the callback with an error response' do
|
83
|
+
expect(described_class::ERROR_RESPONSE).to receive(:dup).and_return(described_class::ERROR_RESPONSE)
|
84
|
+
env = {ERRORS.to_s => []}
|
85
|
+
get(mapped_path, {}, env)
|
86
|
+
|
87
|
+
expect(last_response.body).to eq(described_class::ERROR_RESPONSE.body.join)
|
88
|
+
expect(last_response.headers).to eq(described_class::ERROR_RESPONSE.headers)
|
89
|
+
expect(last_response.status).to eq(described_class::ERROR_RESPONSE.status)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'passes the request mapping' do
|
93
|
+
env = { ERRORS.to_s => []}
|
94
|
+
get(mapped_path, {}, env)
|
95
|
+
expect(last_request.env[REQUEST_MAPPING]).to eq(RequestMapping.new(source_url: "http://example.org#{mapped_path}", mapped_url: current_version_url, mapped_path: mapped_path))
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
it 'translates the header names back in to the http compatible names' do
|
102
|
+
get(mapped_path, {})
|
103
|
+
expect(last_response.headers).to include('Content-Length')
|
104
|
+
expect(last_response.headers).to_not include('CONTENT_LENGTH')
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'adding http_x_forwarded_host header' do
|
108
|
+
context 'when not present in the original request' do
|
109
|
+
it 'appends original request url with port' do
|
110
|
+
get(mapped_path, {})
|
111
|
+
assert_requested :get, current_version_url, headers: {'X-FORWARDED-HOST' => 'example.org:80'}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'when present in the original request' do
|
116
|
+
it 'appends original request url without port' do
|
117
|
+
get(mapped_path, {}, 'HTTP_X_FORWARDED_HOST' => 'staging.com')
|
118
|
+
assert_requested :get, current_version_url, headers: {'X-FORWARDED-HOST' => 'staging.com,staging.com'}
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'preserves the body when forwarding request' do
|
123
|
+
body = {"key" => "value"}
|
124
|
+
stub_request(:put, current_version_url).with(:body => body)
|
125
|
+
|
126
|
+
put(mapped_path, body)
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'preserves the headers when forwarding request' do
|
130
|
+
get(mapped_path, '', 'HTTP_HEADER' => 'value')
|
131
|
+
assert_requested :get, current_version_url, headers: {'Header' => 'value'}
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'sitehub/http_headers'
|
2
|
+
class SiteHub
|
3
|
+
describe HttpHeaders do
|
4
|
+
|
5
|
+
subject do
|
6
|
+
Object.new.tap do |o|
|
7
|
+
o.extend(described_class)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:headers_underscored) do
|
12
|
+
{'CONNECTION' => 'close',
|
13
|
+
'KEEP_ALIVE' => 'something',
|
14
|
+
'PROXY_AUTHENTICATE' => 'something',
|
15
|
+
'PROXY_AUTHORIZATION' => 'something',
|
16
|
+
'TE' => 'something',
|
17
|
+
'TRAILERS' => 'something',
|
18
|
+
'TRANSFER_ENCODING' => 'something',
|
19
|
+
'CONTENT_ENCODING' => 'something',
|
20
|
+
'PROXY_CONNECTION' => 'something'}
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:headers_hyphonised) do
|
24
|
+
Hash.new.tap do |hash|
|
25
|
+
headers_underscored.each do |key, value|
|
26
|
+
hash[key.gsub('_', '-')] = value
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#sanitise_headers' do
|
32
|
+
|
33
|
+
context 'port 80 present in url' do
|
34
|
+
it 'removes the port' do
|
35
|
+
headers_hyphonised['location'] = 'http://mysite.com:80/redirect_endpoint'
|
36
|
+
expect(subject.sanitise_headers(headers_hyphonised)['location']).to eq('http://mysite.com/redirect_endpoint')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'port 443 present in url' do
|
41
|
+
it 'removes the port' do
|
42
|
+
headers_hyphonised['location'] = 'http://mysite.com:443/redirect_endpoint'
|
43
|
+
expect(subject.sanitise_headers(headers_hyphonised)['location']).to eq('http://mysite.com/redirect_endpoint')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'treatment of headers according to RFC2616: 13.5.1 and RFC2616: 14.10' do
|
48
|
+
context 'prohibitted headers hyphonised' do
|
49
|
+
it 'filters them out' do
|
50
|
+
sanatised_headers = subject.sanitise_headers(headers_hyphonised)
|
51
|
+
expect(sanatised_headers.empty?).to eq(true)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'prohibitted headers underscored' do
|
56
|
+
it 'filters them out' do
|
57
|
+
sanatised_headers = subject.sanitise_headers(headers_underscored)
|
58
|
+
expect(sanatised_headers.empty?).to eq(true)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'filters out connections' do
|
63
|
+
headers = subject.sanitise_headers('connection' => 'a, b', 'a' => 'value_a', 'b' => 'value_b', 'c' => 'value_c')
|
64
|
+
expect(headers).to eq('c' => 'value_c')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
describe 'proxying calls' do
|
2
|
+
include_context :site_hub
|
3
|
+
include_context :async
|
4
|
+
|
5
|
+
describe 'supported HTTP verbs' do
|
6
|
+
|
7
|
+
before do
|
8
|
+
WebMock.enable!
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:app){async_middleware.new(rack_application)}
|
12
|
+
|
13
|
+
%i(get post put delete).each do |verb|
|
14
|
+
it "forwards the downstream" do
|
15
|
+
stub_request(verb, downstream_url).to_return(:body => 'hello')
|
16
|
+
send(verb, '/endpoint')
|
17
|
+
expect(app.last_response.body).to eq(['hello'])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'sitehub/logging/access_logger'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
class SiteHub
|
5
|
+
module Logging
|
6
|
+
describe AccessLogger do
|
7
|
+
|
8
|
+
let(:logger) do
|
9
|
+
StringIO.new
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:app) do
|
13
|
+
proc{[200, {}, []]}
|
14
|
+
end
|
15
|
+
|
16
|
+
subject do
|
17
|
+
described_class.new(app, logger)
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#initialize' do
|
21
|
+
|
22
|
+
context 'logger supplied' do
|
23
|
+
it 'sets logger to that logger' do
|
24
|
+
expect(described_class.new(:app, :custom_logger).logger).to eq(LogWrapper.new(:custom_logger))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'logger not supplied' do
|
29
|
+
it 'sets the logger to go to STDERR' do
|
30
|
+
expect(::Logger).to receive(:new).with(STDOUT).and_return(:logging)
|
31
|
+
expect(described_class.new(:app).logger).to eq(LogWrapper.new(:logging))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
#describe '#before_call' do
|
37
|
+
# it 'saves the time' do
|
38
|
+
# now = Time.now
|
39
|
+
# allow(Time).to receive(:now).and_return(now)
|
40
|
+
# subject.before_call(:env)
|
41
|
+
# expect(subject.start_time).to eq(now)
|
42
|
+
# end
|
43
|
+
#end
|
44
|
+
|
45
|
+
describe '#call' do
|
46
|
+
let(:env) do
|
47
|
+
{
|
48
|
+
REQUEST_MAPPING => request_mapping,
|
49
|
+
described_class::QUERY_STRING => '',
|
50
|
+
described_class::PATH_INFO => 'path_info',
|
51
|
+
Constants::RackHttpHeaderKeys::TRANSACTION_ID => :transaction_id
|
52
|
+
}
|
53
|
+
|
54
|
+
end
|
55
|
+
let(:request_mapping) { RequestMapping.new(source_url: :source_url, mapped_url: :mapped_url.to_s, mapped_path: :mapped_path.to_s) }
|
56
|
+
before { subject.call(env) }
|
57
|
+
|
58
|
+
let(:args) { {request_mapping: request_mapping, downstream_response: Rack::Response.new} }
|
59
|
+
it 'logs the request / response details in the required format' do
|
60
|
+
expect(subject).to receive(:log_template).and_return(:template.to_s)
|
61
|
+
expect(logger).to receive(:write).with(:template.to_s)
|
62
|
+
|
63
|
+
subject.call(env)
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'query string' do
|
67
|
+
let(:query_string) { described_class::QUERY_STRING }
|
68
|
+
context 'present' do
|
69
|
+
it 'logs it' do
|
70
|
+
env[query_string] = 'query'
|
71
|
+
subject.call(env)
|
72
|
+
expect(logger.string).to include("?query")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'not present' do
|
77
|
+
it 'is not logged' do
|
78
|
+
subject.call(env)
|
79
|
+
expect(logger.string).to_not include("?")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'logs the transaction id' do
|
85
|
+
subject.call(env)
|
86
|
+
|
87
|
+
expect(logger.string).to include("transaction_id:#{:transaction_id}")
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'logs the response status' do
|
91
|
+
subject.call(env)
|
92
|
+
|
93
|
+
expect(logger.string).to include(args[:downstream_response].status.to_s)
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
it 'logs the downstream url that was proxied to' do
|
98
|
+
subject.call(env)
|
99
|
+
|
100
|
+
expect(logger.string).to include("#{env[described_class::PATH_INFO]} => #{:mapped_url}")
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
describe '#extract_content_length' do
|
105
|
+
context 'content length header not present' do
|
106
|
+
it 'returns -' do
|
107
|
+
expect(subject.extract_content_length({})).to eq('-')
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'content length header not present' do
|
112
|
+
let(:content_length) { described_class::CONTENT_LENGTH }
|
113
|
+
context 'it is 0' do
|
114
|
+
it 'returns -' do
|
115
|
+
expect(subject.extract_content_length({content_length => 0})).to eq('-')
|
116
|
+
end
|
117
|
+
end
|
118
|
+
context 'it is set to a number other than 0'
|
119
|
+
it 'returns the number' do
|
120
|
+
expect(subject.extract_content_length({content_length => 5})).to eq(5)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'sitehub/logging/error_logger'
|
2
|
+
|
3
|
+
class SiteHub
|
4
|
+
module Logging
|
5
|
+
describe ErrorLogger do
|
6
|
+
|
7
|
+
let(:app){ proc{[200, {}, []]}}
|
8
|
+
subject { described_class.new(app) }
|
9
|
+
|
10
|
+
describe '#initialize' do
|
11
|
+
context 'logger supplied' do
|
12
|
+
it 'sets logger to that logger' do
|
13
|
+
expect(described_class.new(:app, :custom_logger).logger).to eq(LogWrapper.new(:custom_logger))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'logger not supplied' do
|
18
|
+
it 'sets the logger to go to STDERR' do
|
19
|
+
expect(::Logger).to receive(:new).with(STDERR).and_return(:logging)
|
20
|
+
expect(described_class.new(:app).logger).to eq(LogWrapper.new(:logging))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#before_call' do
|
26
|
+
it 'adds a collection hold errors' do
|
27
|
+
env = {}
|
28
|
+
subject.call(env)
|
29
|
+
expect(env[ERRORS]).to eq([])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#call' do
|
34
|
+
let(:output) { StringIO.new }
|
35
|
+
let(:logger) { Logger.new(output) }
|
36
|
+
let(:error_message) { 'error' }
|
37
|
+
let(:errors) do
|
38
|
+
Logging::LogStash.new.tap do |stash|
|
39
|
+
stash << error_message
|
40
|
+
end
|
41
|
+
end
|
42
|
+
subject { described_class.new(app, logger) }
|
43
|
+
|
44
|
+
context 'errors have occurred' do
|
45
|
+
it 'logs errors' do
|
46
|
+
log_message = subject.log_message(error: error_message, transaction_id: :transaction_id)
|
47
|
+
subject.call({ERRORS => errors, Constants::RackHttpHeaderKeys::TRANSACTION_ID => :transaction_id})
|
48
|
+
expect(output.string).to eq(log_message)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'no errors have occured' do
|
53
|
+
it 'does not log anything' do
|
54
|
+
expect(logger).to_not receive(:write)
|
55
|
+
subject.call({ERRORS => []})
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#log_message' do
|
61
|
+
let(:error) { 'error' }
|
62
|
+
it 'contains the time and date' do
|
63
|
+
now = Time.now
|
64
|
+
expect(Time).to receive(:now).and_return(now)
|
65
|
+
expected_time = now.strftime("%d/%b/%Y:%H:%M:%S %z")
|
66
|
+
expect(subject.log_message(error: error, transaction_id: :transaction_id)).to start_with("[#{expected_time}]")
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'logs statements made against blah' do
|
70
|
+
|
71
|
+
expect(subject.log_message(error: error, transaction_id: :transaction_id)).to match(/ERROR: .* - ?#{error}$/)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'contains the transation id of the request' do
|
75
|
+
expect(subject.log_message(error: error, transaction_id: :transaction_id)).to include("ERROR: #{:transaction_id}")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'sitehub/logging/log_entry'
|
2
|
+
class SiteHub
|
3
|
+
module Logging
|
4
|
+
describe LogEntry do
|
5
|
+
describe '#initialize' do
|
6
|
+
|
7
|
+
let(:time){Time.now}
|
8
|
+
subject do
|
9
|
+
described_class.new(:message, time)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'sets the message' do
|
13
|
+
expect(subject.message).to be(:message)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'sets the time' do
|
17
|
+
expect(subject.time).to be(time)
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'time not supplied' do
|
21
|
+
subject do
|
22
|
+
described_class.new(:message)
|
23
|
+
end
|
24
|
+
it 'defaults the time' do
|
25
|
+
expect(Time).to receive(:now).and_return(:current_time)
|
26
|
+
expect(subject.time).to be(:current_time)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|