Bluesky app fork with some witchin' additions 馃挮
at main 10 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} 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})