forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {beforeAll, describe, expect, jest, test} from '@jest/globals'
2import * as Sentry from '@sentry/react-native'
3import {nanoid} from 'nanoid/non-secure'
4
5import {Logger} from '#/logger'
6import {sentryTransport} from '#/logger/transports/sentry'
7import {LogLevel} from '#/logger/types'
8
9jest.mock('@sentry/react-native', () => ({
10 addBreadcrumb: jest.fn(),
11 captureException: jest.fn(),
12 captureMessage: jest.fn(),
13}))
14
15beforeAll(() => {
16 jest.useFakeTimers()
17})
18
19describe('general functionality', () => {
20 test('default params', () => {
21 const logger = new Logger()
22 expect(logger.level).toEqual(LogLevel.Info)
23 })
24
25 test('can override default params', () => {
26 const logger = new Logger({
27 level: LogLevel.Debug,
28 })
29 expect(logger.level).toEqual(LogLevel.Debug)
30 })
31
32 test('contextFilter overrides level', () => {
33 const logger = new Logger({
34 level: LogLevel.Info,
35 contextFilter: 'test',
36 })
37 expect(logger.level).toEqual(LogLevel.Debug)
38 })
39
40 test('supports extra metadata', () => {
41 const timestamp = Date.now()
42 const logger = new Logger({})
43
44 const mockTransport = jest.fn()
45
46 logger.addTransport(mockTransport)
47
48 const extra = {foo: true}
49 logger.warn('message', extra)
50
51 expect(mockTransport).toHaveBeenCalledWith(
52 LogLevel.Warn,
53 undefined,
54 'message',
55 extra,
56 timestamp,
57 )
58 })
59
60 test('supports nullish/falsy metadata', () => {
61 const timestamp = Date.now()
62 const logger = new Logger({})
63
64 const mockTransport = jest.fn()
65
66 const remove = logger.addTransport(mockTransport)
67
68 // @ts-expect-error testing the JS case
69 logger.warn('a', null)
70 expect(mockTransport).toHaveBeenCalledWith(
71 LogLevel.Warn,
72 undefined,
73 'a',
74 {},
75 timestamp,
76 )
77
78 // @ts-expect-error testing the JS case
79 logger.warn('b', false)
80 expect(mockTransport).toHaveBeenCalledWith(
81 LogLevel.Warn,
82 undefined,
83 'b',
84 {},
85 timestamp,
86 )
87
88 // @ts-expect-error testing the JS case
89 logger.warn('c', 0)
90 expect(mockTransport).toHaveBeenCalledWith(
91 LogLevel.Warn,
92 undefined,
93 'c',
94 {},
95 timestamp,
96 )
97
98 remove()
99
100 logger.addTransport((level, context, message, metadata) => {
101 expect(typeof metadata).toEqual('object')
102 })
103
104 // @ts-expect-error testing the JS case
105 logger.warn('message', null)
106 })
107
108 test('sentryTransport', () => {
109 const message = 'message'
110 const timestamp = Date.now()
111 const sentryTimestamp = timestamp / 1000
112
113 /*
114 sentryTransport(
115 LogLevel.Debug,
116 Logger.Context.Default,
117 message,
118 {},
119 timestamp,
120 )
121 expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({
122 category: Logger.Context.Default,
123 message,
124 data: {__context__: 'logger'},
125 type: 'default',
126 level: LogLevel.Debug,
127 timestamp: sentryTimestamp,
128 })
129 */
130
131 sentryTransport(
132 LogLevel.Info,
133 Logger.Context.Default,
134 message,
135 {type: 'info', prop: true},
136 timestamp,
137 )
138 expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({
139 category: Logger.Context.Default,
140 message,
141 data: {prop: true, __context__: 'logger'},
142 type: 'info',
143 level: LogLevel.Info,
144 timestamp: sentryTimestamp,
145 })
146
147 sentryTransport(
148 LogLevel.Log,
149 Logger.Context.Default,
150 message,
151 {},
152 timestamp,
153 )
154 expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({
155 category: Logger.Context.Default,
156 message,
157 data: {__context__: 'logger'},
158 type: 'default',
159 level: 'log',
160 timestamp: sentryTimestamp,
161 })
162 jest.runAllTimers()
163 expect(Sentry.captureMessage).toHaveBeenCalledWith(message, {
164 level: 'log',
165 tags: {category: 'logger'},
166 extra: {__context__: 'logger'},
167 })
168
169 sentryTransport(
170 LogLevel.Warn,
171 Logger.Context.Default,
172 message,
173 {},
174 timestamp,
175 )
176 expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({
177 category: Logger.Context.Default,
178 message,
179 data: {__context__: 'logger'},
180 type: 'default',
181 level: 'warning',
182 timestamp: sentryTimestamp,
183 })
184 jest.runAllTimers()
185 expect(Sentry.captureMessage).toHaveBeenCalledWith(message, {
186 level: 'warning',
187 tags: {category: 'logger'},
188 extra: {__context__: 'logger'},
189 })
190
191 const e = new Error('error')
192 const tags = {
193 prop: 'prop',
194 }
195
196 sentryTransport(
197 LogLevel.Error,
198 Logger.Context.Default,
199 e,
200 {
201 tags,
202 prop: true,
203 },
204 timestamp,
205 )
206
207 expect(Sentry.captureException).toHaveBeenCalledWith(e, {
208 tags: {
209 ...tags,
210 category: 'logger',
211 },
212 extra: {
213 prop: true,
214 __context__: 'logger',
215 },
216 })
217 })
218
219 test('sentryTransport serializes errors', () => {
220 const message = 'message'
221 const timestamp = Date.now()
222 const sentryTimestamp = timestamp / 1000
223
224 sentryTransport(
225 LogLevel.Info,
226 undefined,
227 message,
228 {error: new Error('foo')},
229 timestamp,
230 )
231 expect(Sentry.addBreadcrumb).toHaveBeenCalledWith({
232 message,
233 data: {error: 'Error: foo'},
234 type: 'default',
235 level: LogLevel.Info,
236 timestamp: sentryTimestamp,
237 })
238 })
239
240 test('add/remove transport', () => {
241 const timestamp = Date.now()
242 const logger = new Logger({})
243 const mockTransport = jest.fn()
244
245 const remove = logger.addTransport(mockTransport)
246
247 logger.warn('warn')
248
249 remove()
250
251 logger.warn('warn')
252
253 // only called once bc it was removed
254 expect(mockTransport).toHaveBeenNthCalledWith(
255 1,
256 LogLevel.Warn,
257 undefined,
258 'warn',
259 {},
260 timestamp,
261 )
262 })
263})
264
265describe('create', () => {
266 test('create', () => {
267 const mockTransport = jest.fn()
268 const timestamp = Date.now()
269 const message = nanoid()
270 const logger = Logger.create(Logger.Context.Default)
271
272 logger.addTransport(mockTransport)
273 logger.info(message, {})
274
275 expect(mockTransport).toHaveBeenCalledWith(
276 LogLevel.Info,
277 Logger.Context.Default,
278 message,
279 {},
280 timestamp,
281 )
282 })
283})
284
285describe('debug contexts', () => {
286 test('specific', () => {
287 const mockTransport = jest.fn()
288 const timestamp = Date.now()
289 const message = nanoid()
290 const logger = new Logger({
291 // @ts-ignore
292 context: 'specific',
293 level: LogLevel.Debug,
294 })
295
296 logger.addTransport(mockTransport)
297 logger.debug(message, {})
298
299 expect(mockTransport).toHaveBeenCalledWith(
300 LogLevel.Debug,
301 'specific',
302 message,
303 {},
304 timestamp,
305 )
306 })
307
308 test('namespaced', () => {
309 const mockTransport = jest.fn()
310 const timestamp = Date.now()
311 const message = nanoid()
312 const logger = new Logger({
313 // @ts-ignore
314 context: 'namespace:foo',
315 contextFilter: 'namespace:*',
316 level: LogLevel.Debug,
317 })
318
319 logger.addTransport(mockTransport)
320 logger.debug(message, {})
321
322 expect(mockTransport).toHaveBeenCalledWith(
323 LogLevel.Debug,
324 'namespace:foo',
325 message,
326 {},
327 timestamp,
328 )
329 })
330
331 test('ignores inactive', () => {
332 const mockTransport = jest.fn()
333 const timestamp = Date.now()
334 const message = nanoid()
335 const logger = new Logger({
336 // @ts-ignore
337 context: 'namespace:bar:baz',
338 contextFilter: 'namespace:foo:*',
339 })
340
341 logger.addTransport(mockTransport)
342 logger.debug(message, {})
343
344 expect(mockTransport).not.toHaveBeenCalledWith(
345 LogLevel.Debug,
346 'namespace:bar:baz',
347 message,
348 {},
349 timestamp,
350 )
351 })
352})
353
354describe('supports levels', () => {
355 test('debug', () => {
356 const timestamp = Date.now()
357 const logger = new Logger({
358 level: LogLevel.Debug,
359 })
360 const message = nanoid()
361 const mockTransport = jest.fn()
362
363 logger.addTransport(mockTransport)
364
365 logger.debug(message)
366 expect(mockTransport).toHaveBeenCalledWith(
367 LogLevel.Debug,
368 undefined,
369 message,
370 {},
371 timestamp,
372 )
373
374 logger.info(message)
375 expect(mockTransport).toHaveBeenCalledWith(
376 LogLevel.Info,
377 undefined,
378 message,
379 {},
380 timestamp,
381 )
382
383 logger.warn(message)
384 expect(mockTransport).toHaveBeenCalledWith(
385 LogLevel.Warn,
386 undefined,
387 message,
388 {},
389 timestamp,
390 )
391
392 const e = new Error(message)
393 logger.error(e)
394 expect(mockTransport).toHaveBeenCalledWith(
395 LogLevel.Error,
396 undefined,
397 e,
398 {},
399 timestamp,
400 )
401 })
402
403 test('info', () => {
404 const timestamp = Date.now()
405 const logger = new Logger({
406 level: LogLevel.Info,
407 })
408 const message = nanoid()
409 const mockTransport = jest.fn()
410
411 logger.addTransport(mockTransport)
412
413 logger.debug(message)
414 expect(mockTransport).not.toHaveBeenCalled()
415
416 logger.info(message)
417 expect(mockTransport).toHaveBeenCalledWith(
418 LogLevel.Info,
419 undefined,
420 message,
421 {},
422 timestamp,
423 )
424 })
425
426 test('warn', () => {
427 const timestamp = Date.now()
428 const logger = new Logger({
429 level: LogLevel.Warn,
430 })
431 const message = nanoid()
432 const mockTransport = jest.fn()
433
434 logger.addTransport(mockTransport)
435
436 logger.debug(message)
437 expect(mockTransport).not.toHaveBeenCalled()
438
439 logger.info(message)
440 expect(mockTransport).not.toHaveBeenCalled()
441
442 logger.warn(message)
443 expect(mockTransport).toHaveBeenCalledWith(
444 LogLevel.Warn,
445 undefined,
446 message,
447 {},
448 timestamp,
449 )
450 })
451
452 test('error', () => {
453 const timestamp = Date.now()
454 const logger = new Logger({
455 level: LogLevel.Error,
456 })
457 const message = nanoid()
458 const mockTransport = jest.fn()
459
460 logger.addTransport(mockTransport)
461
462 logger.debug(message)
463 expect(mockTransport).not.toHaveBeenCalled()
464
465 logger.info(message)
466 expect(mockTransport).not.toHaveBeenCalled()
467
468 logger.warn(message)
469 expect(mockTransport).not.toHaveBeenCalled()
470
471 const e = new Error('original message')
472 logger.error(e)
473 expect(mockTransport).toHaveBeenCalledWith(
474 LogLevel.Error,
475 undefined,
476 e,
477 {},
478 timestamp,
479 )
480 })
481})