lenjador 1.2.1 → 2.1.0

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