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