lenjador 1.2.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,319 @@
1
+ require 'spec_helper'
2
+ require_relative '../../../lib/lenjador/preprocessors/whitelist'
3
+
4
+ RSpec.describe Lenjador::Preprocessors::Whitelist do
5
+ context 'when :action is :mask or omitted' do
6
+ subject(:processed_data) { described_class.new(config).process(data) }
7
+
8
+ let(:config) { {pointers: pointers} }
9
+ let(:pointers) { [] }
10
+ let(:data) do
11
+ {
12
+ field: 'secret',
13
+ data: {
14
+ field: 'secret'
15
+ },
16
+ array: [{field: 'secret'}]
17
+ }
18
+ end
19
+
20
+ it 'masks all non-whitelisted fields' do
21
+ expect(processed_data).to eq(
22
+ field: '*****',
23
+ data: '*****',
24
+ array: '*****'
25
+ )
26
+ end
27
+
28
+ context 'when pointer has trailing slash' do
29
+ let(:pointers) { ['/field/'] }
30
+
31
+ it 'throws exception' do
32
+ expect { processed_data }.to raise_exception(Lenjador::Preprocessors::Whitelist::InvalidPointerFormatException)
33
+ end
34
+ end
35
+
36
+ context 'with whitelisted field' do
37
+ let(:pointers) { ['/field'] }
38
+
39
+ it 'includes the field' do
40
+ expect(processed_data).to eq(
41
+ field: 'secret',
42
+ data: '*****',
43
+ array: '*****'
44
+ )
45
+ end
46
+ end
47
+
48
+ context 'with whitelisted nested field' do
49
+ let(:pointers) { ['/data/field'] }
50
+
51
+ it 'includes nested field' do
52
+ expect(processed_data).to eq(
53
+ field: '*****',
54
+ data: {
55
+ field: 'secret'
56
+ },
57
+ array: '*****'
58
+ )
59
+ end
60
+ end
61
+
62
+ context 'with whitelisted array element field' do
63
+ let(:pointers) { ['/array/0/field'] }
64
+
65
+ it 'includes array element' do
66
+ expect(processed_data).to eq(
67
+ field: '*****',
68
+ data: '*****',
69
+ array: [{field: 'secret'}]
70
+ )
71
+ end
72
+ end
73
+
74
+ context 'with whitelisted hash' do
75
+ it 'includes all whitelisted hash elements' do
76
+ source = {foo: {bar: 'baz'}}
77
+ target = {foo: {bar: 'baz'}}
78
+ expect(process(['/foo/~'], source)).to eq(target)
79
+ end
80
+
81
+ it 'does not include nested elements' do
82
+ source = {foo: {bar: {baz: 'asd'}}}
83
+ target = {foo: {bar: {baz: '*****'}}}
84
+ expect(process(['/foo/~'], source)).to eq(target)
85
+ end
86
+ end
87
+
88
+ context 'with whitelisted array elements field with wildcard' do
89
+ let(:data) do
90
+ {
91
+ array: [
92
+ {field: 'data1', secret: 'secret1'},
93
+ {field: 'data2', secret: 'secret2'}
94
+ ]
95
+ }
96
+ end
97
+ let(:pointers) { ['/array/~/field'] }
98
+
99
+ it 'includes array elements field' do
100
+ expect(processed_data).to include(
101
+ array: [
102
+ {field: 'data1', secret: '*****'},
103
+ {field: 'data2', secret: '*****'}
104
+ ]
105
+ )
106
+ end
107
+ end
108
+
109
+ context 'with whitelisted string array elements with wildcard' do
110
+ let(:data) do
111
+ {array: %w[secret secret]}
112
+ end
113
+ let(:pointers) { ['/array/~'] }
114
+
115
+ it 'includes array elements' do
116
+ expect(processed_data).to include(array: %w[secret secret])
117
+ end
118
+ end
119
+
120
+ context 'with whitelisted string array elements in an array with wildcard' do
121
+ let(:data) do
122
+ {
123
+ nested: [{array: %w[secret secret]}]
124
+ }
125
+ end
126
+ let(:pointers) { ['/nested/~/array/~'] }
127
+
128
+ it 'includes array elements' do
129
+ expect(processed_data).to include(nested: [{array: %w[secret secret]}])
130
+ end
131
+ end
132
+
133
+ context 'with whitelisted array element' do
134
+ let(:pointers) { ['/array/0'] }
135
+
136
+ it 'masks array element' do
137
+ expect(processed_data).to include(array: [{field: '*****'}])
138
+ end
139
+ end
140
+
141
+ context 'with whitelisted array' do
142
+ let(:pointers) { ['/array'] }
143
+
144
+ it 'masks array' do
145
+ expect(processed_data).to include(array: ['*****'])
146
+ end
147
+ end
148
+
149
+ context 'when boolean present' do
150
+ let(:data) { {bool: true} }
151
+
152
+ it 'masks it with asteriks' do
153
+ expect(processed_data).to eq(bool: '*****')
154
+ end
155
+ end
156
+
157
+ context 'when field has slash in the name' do
158
+ let(:data) do
159
+ {'field_with_/' => 'secret'}
160
+ end
161
+ let(:pointers) { ['/field_with_~1'] }
162
+
163
+ it 'includes field' do
164
+ expect(processed_data).to include('field_with_/' => 'secret')
165
+ end
166
+ end
167
+
168
+ context 'when field has tilde in the name' do
169
+ let(:data) do
170
+ {'field_with_~' => 'secret'}
171
+ end
172
+ let(:pointers) { ['/field_with_~0'] }
173
+
174
+ it 'includes field' do
175
+ expect(processed_data).to include('field_with_~' => 'secret')
176
+ end
177
+ end
178
+
179
+ context 'when field has tilde and 1' do
180
+ let(:data) do
181
+ {'field_with_~1' => 'secret'}
182
+ end
183
+ let(:pointers) { ['/field_with_~01'] }
184
+
185
+ it 'includes field' do
186
+ expect(processed_data).to include('field_with_~1' => 'secret')
187
+ end
188
+ end
189
+
190
+ def process(pointers, data)
191
+ described_class.new(pointers: pointers).process(data)
192
+ end
193
+ end
194
+
195
+ context 'when :action is :exclude or :prune' do
196
+ subject(:processed_data) { described_class.new(config).process(data) }
197
+
198
+ let(:config) { {pointers: pointers, action: :prune} }
199
+ let(:pointers) { [] }
200
+ let(:data) do
201
+ {
202
+ field: 'secret',
203
+ data: {
204
+ field: 'secret'
205
+ },
206
+ array: [{field: 'secret'}, {field2: 'secret'}]
207
+ }
208
+ end
209
+
210
+ context 'when pointers is empty' do
211
+ it 'prunes all fields from the input' do
212
+ expect(processed_data).to eq({})
213
+ end
214
+ end
215
+
216
+ context 'with whitelisted field' do
217
+ let(:pointers) { ['/field'] }
218
+
219
+ it 'includes the field' do
220
+ expect(processed_data).to eq(field: 'secret')
221
+ end
222
+ end
223
+
224
+ context 'with whitelisted nested field' do
225
+ let(:pointers) { ['/data/field'] }
226
+
227
+ it 'includes nested field' do
228
+ expect(processed_data).to eq(data: {field: 'secret'})
229
+ end
230
+ end
231
+
232
+ context 'with whitelisted array element field' do
233
+ let(:pointers) { ['/array/0/field'] }
234
+
235
+ it 'includes array element' do
236
+ expect(processed_data).to eq(array: [{field: 'secret'}])
237
+ end
238
+ end
239
+
240
+ context 'with whitelisted hash' do
241
+ it 'includes all whitelisted hash elements' do
242
+ source = {foo: {bar: 'baz'}}
243
+ target = {foo: {bar: 'baz'}}
244
+ expect(process(['/foo/~'], source)).to eq(target)
245
+ end
246
+
247
+ it 'does not include nested elements' do
248
+ source = {foo: {bar: {baz: 'asd'}}}
249
+ target = {foo: {bar: {}}}
250
+ expect(process(['/foo/~'], source)).to eq(target)
251
+ end
252
+ end
253
+
254
+ context 'with whitelisted array elements field with wildcard' do
255
+ let(:data) do
256
+ {
257
+ array: [
258
+ {field: 'data1', secret: 'secret1'},
259
+ {field: 'data2', secret: 'secret2'}
260
+ ]
261
+ }
262
+ end
263
+ let(:pointers) { ['/array/~/field'] }
264
+
265
+ it 'includes array elements field' do
266
+ expect(processed_data).to include(
267
+ array: [
268
+ {field: 'data1'},
269
+ {field: 'data2'}
270
+ ]
271
+ )
272
+ end
273
+ end
274
+
275
+ context 'with whitelisted string array elements with wildcard' do
276
+ let(:data) do
277
+ {array: %w[secret1 secret2]}
278
+ end
279
+ let(:pointers) { ['/array/~'] }
280
+
281
+ it 'includes array elements' do
282
+ expect(processed_data).to include(array: %w[secret1 secret2])
283
+ end
284
+ end
285
+
286
+ context 'with whitelisted string array elements in an array with wildcard' do
287
+ let(:data) do
288
+ {
289
+ nested: [{array: %w[secret1 secret2]}]
290
+ }
291
+ end
292
+ let(:pointers) { ['/nested/~/array/~'] }
293
+
294
+ it 'includes array elements' do
295
+ expect(processed_data).to include(nested: [{array: %w[secret1 secret2]}])
296
+ end
297
+ end
298
+
299
+ context 'with whitelisted array element' do
300
+ let(:pointers) { ['/array/0'] }
301
+
302
+ it 'masks array element' do
303
+ expect(processed_data).to eq(array: [{}])
304
+ end
305
+ end
306
+
307
+ context 'with whitelisted array' do
308
+ let(:pointers) { ['/array'] }
309
+
310
+ it 'masks array' do
311
+ expect(processed_data).to include(array: [])
312
+ end
313
+ end
314
+
315
+ def process(pointers, data)
316
+ described_class.new(pointers: pointers, action: :prune).process(data)
317
+ end
318
+ end
319
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Lenjador::Utils do
6
+ let(:now) { Time.utc(2015, 10, 11, 23, 10, 21, 123_456) }
7
+
8
+ before do
9
+ allow(Time).to receive(:now) { now }
10
+ end
11
+
12
+ describe '.build_event' do
13
+ subject(:event) { described_class.build_event(metadata, level, application_name) }
14
+
15
+ let(:application_name) { 'test_service' }
16
+ let(:level) { :info }
17
+ let(:metadata) { {x: 'y'} }
18
+
19
+ it 'includes it in the event as application' do
20
+ expect(event[:application]).to eq(application_name)
21
+ end
22
+
23
+ it 'includes log level' do
24
+ expect(event[:level]).to eq(:info)
25
+ end
26
+
27
+ it 'includes timestamp' do
28
+ expect(event[:@timestamp]).to eq(now)
29
+ end
30
+
31
+ context 'when @timestamp provided' do
32
+ let(:metadata) { {message: 'test', :@timestamp => 'a timestamp'} }
33
+
34
+ it 'overwrites @timestamp' do
35
+ expect(event[:message]).to eq('test')
36
+ expect(event[:@timestamp]).to eq('a timestamp')
37
+ end
38
+ end
39
+
40
+ context 'when host provided' do
41
+ let(:metadata) { {message: 'test', host: 'xyz'} }
42
+
43
+ it 'overwrites host' do
44
+ expect(event[:message]).to eq('test')
45
+ expect(event[:host]).to eq('xyz')
46
+ end
47
+ end
48
+
49
+ context 'when OpenTracing is defined' do # rubocop:disable RSpec/MultipleMemoizedHelpers
50
+ let(:trace_id) { 'trace-id' }
51
+ let(:span_id) { 'span-id' }
52
+
53
+ it 'includes tracing data in the event when active span is present' do
54
+ span_context = double(trace_id: trace_id, span_id: span_id)
55
+ span = double(context: span_context)
56
+ open_tracing = double(active_span: span)
57
+ stub_const('OpenTracing', open_tracing)
58
+
59
+ expect(event).to include(
60
+ trace_id: trace_id,
61
+ span_id: span_id
62
+ )
63
+ end
64
+
65
+ it 'does not include tracing data if active span is not present' do
66
+ open_tracing = double(active_span: nil)
67
+ stub_const('OpenTracing', open_tracing)
68
+
69
+ expect(event).not_to include(:trace_id, :span_id)
70
+ end
71
+
72
+ it 'does not include tracing data if active span does not respond to trace_id' do
73
+ span_context = double(span_id: span_id)
74
+ span = double(context: span_context)
75
+ open_tracing = double(active_span: span)
76
+ stub_const('OpenTracing', open_tracing)
77
+
78
+ expect(event).not_to include(:trace_id, :span_id)
79
+ end
80
+
81
+ it 'does not include tracing data if active span does not respond to span_id' do
82
+ span_context = double(trace_id: trace_id)
83
+ span = double(context: span_context)
84
+ open_tracing = double(active_span: span)
85
+ stub_const('OpenTracing', open_tracing)
86
+
87
+ expect(event).not_to include(:trace_id, :span_id)
88
+ end
89
+ end
90
+ end
91
+
92
+ describe '.generate_json' do
93
+ it 'serializes time objects to iso8601' do
94
+ serialized_time = now.iso8601(3)
95
+ json = described_class.generate_json(time: now)
96
+ expect(json).to eq(%({"time":"#{serialized_time}"}))
97
+ end
98
+ end
99
+ end
@@ -3,34 +3,31 @@ require 'spec_helper'
3
3
  describe Lenjador do
4
4
  describe '.build' do
5
5
  it 'creates stdout logger' do
6
- expect(described_class).to receive(:new) do |adapters|
7
- expect(adapters.count).to be(1)
8
- expect(adapters.first).to be_a(described_class::Adapters::StdoutAdapter)
6
+ expect(described_class).to receive(:new) do |adapter|
7
+ expect(adapter).to be_a(described_class::Adapters::StdoutAdapter)
9
8
  end
10
9
 
11
- described_class.build('test_service', stdout: nil)
10
+ described_class.build('test_service', {})
12
11
  end
13
12
 
14
13
  it 'creates stdout json logger' do
15
- expect(described_class).to receive(:new) do |adapters|
16
- expect(adapters.count).to be(1)
17
- expect(adapters.first).to be_a(described_class::Adapters::StdoutJsonAdapter)
14
+ expect(described_class).to receive(:new) do |adapter|
15
+ expect(adapter).to be_a(described_class::Adapters::StdoutJsonAdapter)
18
16
  end
19
17
 
20
- described_class.build('test_service', stdout: {json: true})
18
+ described_class.build('test_service', json: true)
21
19
  end
22
20
 
23
21
  it 'creates stdout logger when no loggers are specified' do
24
- expect(described_class).to receive(:new) do |adapters|
25
- expect(adapters.count).to be(1)
26
- expect(adapters.first).to be_a(described_class::Adapters::StdoutAdapter)
22
+ expect(described_class).to receive(:new) do |adapter|
23
+ expect(adapter).to be_a(described_class::Adapters::StdoutAdapter)
27
24
  end
28
25
 
29
26
  described_class.build('test_service', nil)
30
27
  end
31
28
 
32
29
  it 'creates preprocessor when preprocessor defined' do
33
- expect(described_class).to receive(:new) do |adapters, preprocessors|
30
+ expect(described_class).to receive(:new) do |_adapter, _level, preprocessors|
34
31
  expect(preprocessors.count).to be(1)
35
32
  expect(preprocessors.first).to be_a(described_class::Preprocessors::Blacklist)
36
33
  end
@@ -41,74 +38,74 @@ describe Lenjador do
41
38
  end
42
39
 
43
40
  context 'when preprocessor defined' do
44
- let(:lenjador) { described_class.new([adapter], [preprocessor]) }
41
+ let(:lenjador) { described_class.new(adapter, level, [preprocessor]) }
45
42
  let(:adapter) { double }
43
+ let(:level) { Lenjador::Severity::DEBUG }
46
44
  let(:preprocessor) { double }
47
45
  let(:data) { {data: 'data'} }
48
46
 
49
47
  it 'preprocesses data before logging' do
50
48
  expect(preprocessor).to receive(:process).with(data).and_return(data.merge(processed: true)).ordered
51
- expect(adapter).to receive(:log).with(:info, data.merge(processed: true)).ordered
49
+ expect(adapter).to receive(:log).with(described_class::Severity::INFO, data.merge(processed: true)).ordered
52
50
 
53
51
  lenjador.info(data)
54
52
  end
55
53
  end
56
54
 
57
55
  context 'when parsing log data' do
58
- let(:lenjador) { described_class.new([adapter], preprocessors) }
56
+ let(:lenjador) { described_class.new(adapter, level, preprocessors) }
59
57
  let(:adapter) { double }
58
+ let(:level) { Lenjador::Severity::DEBUG }
60
59
  let(:preprocessors) { [] }
61
60
 
62
61
  it 'parses empty string with nil metadata' do
63
- expect(adapter).to receive(:log).with(:info, message: '')
62
+ expect(adapter).to receive(:log).with(described_class::Severity::INFO, message: '')
64
63
 
65
64
  lenjador.info('', nil)
66
65
  end
67
66
 
68
67
  it 'parses nil as metadata' do
69
- expect(adapter).to receive(:log).with(:info, message: nil)
68
+ expect(adapter).to receive(:log).with(described_class::Severity::INFO, message: nil)
70
69
 
71
70
  lenjador.info(nil)
72
71
  end
73
72
 
74
73
  it 'parses only message' do
75
- expect(adapter).to receive(:log).with(:info, message: 'test message')
74
+ expect(adapter).to receive(:log).with(described_class::Severity::INFO, message: 'test message')
76
75
 
77
76
  lenjador.info 'test message'
78
77
  end
79
78
 
80
79
  it 'parses only metadata' do
81
- expect(adapter).to receive(:log).with(:info, test: 'data')
80
+ expect(adapter).to receive(:log).with(described_class::Severity::INFO, test: 'data')
82
81
 
83
82
  lenjador.info test: 'data'
84
83
  end
85
84
 
86
85
  it 'parses message and metadata' do
87
- expect(adapter).to receive(:log).with(:info, message: 'test message', test: 'data')
86
+ expect(adapter).to receive(:log).with(described_class::Severity::INFO, message: 'test message', test: 'data')
88
87
 
89
88
  lenjador.info 'test message', test: 'data'
90
89
  end
91
90
 
92
91
  it 'parses block as a message' do
93
92
  message = 'test message'
94
- expect(adapter).to receive(:log).with(:info, message: message)
93
+ expect(adapter).to receive(:log).with(described_class::Severity::INFO, message: message)
95
94
 
96
95
  lenjador.info { message }
97
96
  end
98
97
 
99
98
  it 'ignores progname on block syntax' do
100
99
  message = 'test message'
101
- expect(adapter).to receive(:log).with(:info, message: message)
100
+ expect(adapter).to receive(:log).with(described_class::Severity::INFO, message: message)
102
101
 
103
102
  lenjador.info('progname') { message }
104
103
  end
105
104
  end
106
105
 
107
- context 'log level queries' do
106
+ context 'with log level' do
108
107
  context 'when adapter has debug level' do
109
- let(:logger) do
110
- described_class.build('test_service', stdout: {level: 'debug'})
111
- end
108
+ let(:logger) { described_class.build('test_service', level: 'debug') }
112
109
 
113
110
  it 'responds true to debug? and higher levels' do
114
111
  expect(logger.debug?).to be(true)
@@ -120,9 +117,7 @@ describe Lenjador do
120
117
  end
121
118
 
122
119
  context 'when adapter has info level' do
123
- let(:logger) do
124
- described_class.build('test_service', stdout: {level: 'info'})
125
- end
120
+ let(:logger) { described_class.build('test_service', level: 'info') }
126
121
 
127
122
  it 'responds true to info? and higher levels' do
128
123
  expect(logger.debug?).to be(false)
@@ -134,9 +129,22 @@ describe Lenjador do
134
129
  end
135
130
  end
136
131
 
137
- it 'has the same interface as Ruby logger' do
138
- skip "https://salemove.atlassian.net/browse/INF-464"
139
- logger = described_class.build('test_service', stdout: {level: 'debug'})
140
- expect(logger).to implement_interface(Logger)
132
+ it 'allows changing log level on existing instance' do
133
+ logger = described_class.build('test_service', level: 'info')
134
+
135
+ logger.level = ::Logger::DEBUG
136
+ expect(logger.debug?).to eq(true)
137
+
138
+ logger.level = ::Logger::INFO
139
+ expect(logger.debug?).to eq(false)
140
+ expect(logger.info?).to eq(true)
141
+
142
+ logger.level = ::Logger::WARN
143
+ expect(logger.info?).to eq(false)
144
+ expect(logger.warn?).to eq(true)
145
+
146
+ logger.level = ::Logger::ERROR
147
+ expect(logger.warn?).to eq(false)
148
+ expect(logger.error?).to eq(true)
141
149
  end
142
150
  end