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