1
1
import { describe , expect , it } from 'vitest' ;
2
2
import {
3
+ getHttpSpanDetailsFromUrlObject ,
3
4
getSanitizedUrlString ,
4
5
getSanitizedUrlStringFromUrlObject ,
5
6
isURLObjectRelative ,
@@ -344,6 +345,48 @@ describe('getSanitizedUrlStringFromUrlObject', () => {
344
345
[ 'url with port 4433' , 'http://172.31.12.144:4433/test' , 'http://172.31.12.144:4433/test' ] ,
345
346
[ 'url with port 443' , 'http://172.31.12.144:443/test' , 'http://172.31.12.144/test' ] ,
346
347
[ 'url with IP and port 80' , 'http://172.31.12.144:80/test' , 'http://172.31.12.144/test' ] ,
348
+ [ 'invalid URL' , 'invalid-url' , '/invalid-url' ] ,
349
+ [ 'valid absolute URL with base' , 'https://somedomain.com' , 'https://somedomain.com/' ] ,
350
+ [ 'relative URL' , '/path/to/happiness' , '/path/to/happiness' ] ,
351
+ [ 'relative URL with query' , '/path/to/happiness?q=1' , '/path/to/happiness' ] ,
352
+ [ 'relative URL with hash' , '/path/to/happiness#section' , '/path/to/happiness' ] ,
353
+ [ 'relative URL with query and hash' , '/path/to/happiness?q=1#section' , '/path/to/happiness' ] ,
354
+ [
355
+ 'URL with special chars' ,
356
+ 'https://somedomain.com/path/with spaces/and/special@chars' ,
357
+ 'https://somedomain.com/path/with%20spaces/and/special@chars' ,
358
+ ] ,
359
+ [
360
+ 'URL with unicode' ,
361
+ 'https://somedomain.com/path/with/unicode/测试' ,
362
+ 'https://somedomain.com/path/with/unicode/%E6%B5%8B%E8%AF%95' ,
363
+ ] ,
364
+ [ 'URL with multiple query params' , 'https://somedomain.com/path?q1=1&q2=2&q3=3' , 'https://somedomain.com/path' ] ,
365
+ [ 'URL with encoded chars' , 'https://somedomain.com/path/%20%2F%3F%23' , 'https://somedomain.com/path/%20%2F%3F%23' ] ,
366
+ [ 'URL with IPv4' , 'https://192.168.1.1/path' , 'https://192.168.1.1/path' ] ,
367
+ [ 'URL with IPv6' , 'https://[2001:db8::1]/path' , 'https://[2001:db8::1]/path' ] ,
368
+ [ 'URL with subdomain' , 'https://sub.somedomain.com/path' , 'https://sub.somedomain.com/path' ] ,
369
+ [ 'URL with multiple subdomains' , 'https://sub1.sub2.somedomain.com/path' , 'https://sub1.sub2.somedomain.com/path' ] ,
370
+ [ 'URL with trailing slash' , 'https://somedomain.com/path/' , 'https://somedomain.com/path/' ] ,
371
+ [ 'URL with empty path' , 'https://somedomain.com' , 'https://somedomain.com/' ] ,
372
+ [ 'URL with root path' , 'https://somedomain.com/' , 'https://somedomain.com/' ] ,
373
+ [ 'URL with file extension' , 'https://somedomain.com/path/file.html' , 'https://somedomain.com/path/file.html' ] ,
374
+ [ 'URL with custom protocol' , 'custom://somedomain.com/path' , 'custom://somedomain.com/path' ] ,
375
+ [
376
+ 'URL with query containing special chars' ,
377
+ 'https://somedomain.com/path?q=hello+world&x=1/2' ,
378
+ 'https://somedomain.com/path' ,
379
+ ] ,
380
+ [
381
+ 'URL with hash containing special chars' ,
382
+ 'https://somedomain.com/path#section/1/2' ,
383
+ 'https://somedomain.com/path' ,
384
+ ] ,
385
+ [
386
+ 'URL with all components' ,
387
+ 'https://user:[email protected] :8080/path/file.html?q=1#section' ,
388
+ 'https://%filtered%:%filtered%@sub.somedomain.com:8080/path/file.html' ,
389
+ ] ,
347
390
] ) ( 'returns a sanitized URL for a %s' , ( _ , rawUrl : string , sanitizedURL : string ) => {
348
391
const urlObject = parseStringToURLObject ( rawUrl ) ;
349
392
if ( ! urlObject ) {
@@ -352,3 +395,227 @@ describe('getSanitizedUrlStringFromUrlObject', () => {
352
395
expect ( getSanitizedUrlStringFromUrlObject ( urlObject ) ) . toEqual ( sanitizedURL ) ;
353
396
} ) ;
354
397
} ) ;
398
+
399
+ describe ( 'getHttpSpanDetailsFromUrlObject' , ( ) => {
400
+ it ( 'handles undefined URL object' , ( ) => {
401
+ const [ name , attributes ] = getHttpSpanDetailsFromUrlObject ( undefined , 'test-origin' ) ;
402
+ expect ( name ) . toBe ( 'GET /' ) ;
403
+ expect ( attributes ) . toEqual ( {
404
+ 'sentry.origin' : 'test-origin' ,
405
+ 'sentry.source' : 'url' ,
406
+ } ) ;
407
+ } ) ;
408
+
409
+ it ( 'handles relative URL object' , ( ) => {
410
+ const urlObject = parseStringToURLObject ( '/api/users' ) ! ;
411
+ const [ name , attributes ] = getHttpSpanDetailsFromUrlObject ( urlObject , 'test-origin' ) ;
412
+ expect ( name ) . toBe ( 'GET /api/users' ) ;
413
+ expect ( attributes ) . toEqual ( {
414
+ 'sentry.origin' : 'test-origin' ,
415
+ 'sentry.source' : 'url' ,
416
+ 'url.path' : '/api/users' ,
417
+ } ) ;
418
+ } ) ;
419
+
420
+ it ( 'handles absolute URL object' , ( ) => {
421
+ const urlObject = parseStringToURLObject ( 'https://example.com/api/users?q=test#section' ) ! ;
422
+ const [ name , attributes ] = getHttpSpanDetailsFromUrlObject ( urlObject , 'test-origin' ) ;
423
+ expect ( name ) . toBe ( 'GET https://example.com/api/users' ) ;
424
+ expect ( attributes ) . toEqual ( {
425
+ 'sentry.origin' : 'test-origin' ,
426
+ 'sentry.source' : 'url' ,
427
+ 'url.path' : '/api/users' ,
428
+ 'url.query' : '?q=test' ,
429
+ 'url.fragment' : '#section' ,
430
+ 'url.full' : 'https://example.com/api/users?q=test#section' ,
431
+ 'server.address' : 'example.com' ,
432
+ 'url.scheme' : 'https:' ,
433
+ } ) ;
434
+ } ) ;
435
+
436
+ it ( 'handles URL object with request method' , ( ) => {
437
+ const urlObject = parseStringToURLObject ( 'https://example.com/api/users' ) ! ;
438
+ const [ name , attributes ] = getHttpSpanDetailsFromUrlObject ( urlObject , 'test-origin' , { method : 'POST' } ) ;
439
+ expect ( name ) . toBe ( 'POST https://example.com/api/users' ) ;
440
+ expect ( attributes ) . toEqual ( {
441
+ 'sentry.origin' : 'test-origin' ,
442
+ 'sentry.source' : 'url' ,
443
+ 'url.path' : '/api/users' ,
444
+ 'url.full' : 'https://example.com/api/users' ,
445
+ 'server.address' : 'example.com' ,
446
+ 'url.scheme' : 'https:' ,
447
+ 'http.request.method' : 'POST' ,
448
+ } ) ;
449
+ } ) ;
450
+
451
+ it ( 'handles URL object with route name' , ( ) => {
452
+ const urlObject = parseStringToURLObject ( 'https://example.com/api/users' ) ! ;
453
+ const [ name , attributes ] = getHttpSpanDetailsFromUrlObject ( urlObject , 'test-origin' , undefined , '/api/users/:id' ) ;
454
+ expect ( name ) . toBe ( 'GET /api/users/:id' ) ;
455
+ expect ( attributes ) . toEqual ( {
456
+ 'sentry.origin' : 'test-origin' ,
457
+ 'sentry.source' : 'route' ,
458
+ 'url.path' : '/api/users' ,
459
+ 'url.full' : 'https://example.com/api/users' ,
460
+ 'server.address' : 'example.com' ,
461
+ 'url.scheme' : 'https:' ,
462
+ 'http.route' : '/api/users/:id' ,
463
+ } ) ;
464
+ } ) ;
465
+
466
+ it ( 'handles root path URL' , ( ) => {
467
+ const urlObject = parseStringToURLObject ( 'https://example.com/' ) ! ;
468
+ const [ name , attributes ] = getHttpSpanDetailsFromUrlObject ( urlObject , 'test-origin' ) ;
469
+ expect ( name ) . toBe ( 'GET https://example.com/' ) ;
470
+ expect ( attributes ) . toEqual ( {
471
+ 'sentry.origin' : 'test-origin' ,
472
+ 'sentry.source' : 'route' ,
473
+ 'url.path' : '/' ,
474
+ 'url.full' : 'https://example.com/' ,
475
+ 'server.address' : 'example.com' ,
476
+ 'url.scheme' : 'https:' ,
477
+ } ) ;
478
+ } ) ;
479
+
480
+ it ( 'handles URL with port' , ( ) => {
481
+ const urlObject = parseStringToURLObject ( 'https://example.com:8080/api/users' ) ! ;
482
+ const [ name , attributes ] = getHttpSpanDetailsFromUrlObject ( urlObject , 'test-origin' ) ;
483
+ expect ( name ) . toBe ( 'GET https://example.com:8080/api/users' ) ;
484
+ expect ( attributes ) . toEqual ( {
485
+ 'sentry.origin' : 'test-origin' ,
486
+ 'sentry.source' : 'url' ,
487
+ 'url.path' : '/api/users' ,
488
+ 'url.full' : 'https://example.com:8080/api/users' ,
489
+ 'server.address' : 'example.com' ,
490
+ 'url.scheme' : 'https:' ,
491
+ 'url.port' : '8080' ,
492
+ } ) ;
493
+ } ) ;
494
+
495
+ it ( 'handles URL with non-standard port and request method' , ( ) => {
496
+ const urlObject = parseStringToURLObject ( 'https://example.com:3000/api/users' ) ! ;
497
+ const [ name , attributes ] = getHttpSpanDetailsFromUrlObject ( urlObject , 'test-origin' , { method : 'PUT' } ) ;
498
+ expect ( name ) . toBe ( 'PUT https://example.com:3000/api/users' ) ;
499
+ expect ( attributes ) . toEqual ( {
500
+ 'sentry.origin' : 'test-origin' ,
501
+ 'sentry.source' : 'url' ,
502
+ 'url.path' : '/api/users' ,
503
+ 'url.full' : 'https://example.com:3000/api/users' ,
504
+ 'server.address' : 'example.com' ,
505
+ 'url.scheme' : 'https:' ,
506
+ 'url.port' : '3000' ,
507
+ 'http.request.method' : 'PUT' ,
508
+ } ) ;
509
+ } ) ;
510
+
511
+ it ( 'handles URL with route name and request method' , ( ) => {
512
+ const urlObject = parseStringToURLObject ( 'https://example.com/api/users/123' ) ! ;
513
+ const [ name , attributes ] = getHttpSpanDetailsFromUrlObject (
514
+ urlObject ,
515
+ 'test-origin' ,
516
+ { method : 'PATCH' } ,
517
+ '/api/users/:id' ,
518
+ ) ;
519
+ expect ( name ) . toBe ( 'PATCH /api/users/:id' ) ;
520
+ expect ( attributes ) . toEqual ( {
521
+ 'sentry.origin' : 'test-origin' ,
522
+ 'sentry.source' : 'route' ,
523
+ 'url.path' : '/api/users/123' ,
524
+ 'url.full' : 'https://example.com/api/users/123' ,
525
+ 'server.address' : 'example.com' ,
526
+ 'url.scheme' : 'https:' ,
527
+ 'http.route' : '/api/users/:id' ,
528
+ 'http.request.method' : 'PATCH' ,
529
+ } ) ;
530
+ } ) ;
531
+
532
+ it ( 'handles URL with query params and route name' , ( ) => {
533
+ const urlObject = parseStringToURLObject ( 'https://example.com/api/search?q=test&page=1' ) ! ;
534
+ const [ name , attributes ] = getHttpSpanDetailsFromUrlObject ( urlObject , 'test-origin' , undefined , '/api/search' ) ;
535
+ expect ( name ) . toBe ( 'GET /api/search' ) ;
536
+ expect ( attributes ) . toEqual ( {
537
+ 'sentry.origin' : 'test-origin' ,
538
+ 'sentry.source' : 'route' ,
539
+ 'url.path' : '/api/search' ,
540
+ 'url.query' : '?q=test&page=1' ,
541
+ 'url.full' : 'https://example.com/api/search?q=test&page=1' ,
542
+ 'server.address' : 'example.com' ,
543
+ 'url.scheme' : 'https:' ,
544
+ 'http.route' : '/api/search' ,
545
+ } ) ;
546
+ } ) ;
547
+
548
+ it ( 'handles URL with fragment and route name' , ( ) => {
549
+ const urlObject = parseStringToURLObject ( 'https://example.com/api/docs#section-1' ) ! ;
550
+ const [ name , attributes ] = getHttpSpanDetailsFromUrlObject ( urlObject , 'test-origin' , undefined , '/api/docs' ) ;
551
+ expect ( name ) . toBe ( 'GET /api/docs' ) ;
552
+ expect ( attributes ) . toEqual ( {
553
+ 'sentry.origin' : 'test-origin' ,
554
+ 'sentry.source' : 'route' ,
555
+ 'url.path' : '/api/docs' ,
556
+ 'url.fragment' : '#section-1' ,
557
+ 'url.full' : 'https://example.com/api/docs#section-1' ,
558
+ 'server.address' : 'example.com' ,
559
+ 'url.scheme' : 'https:' ,
560
+ 'http.route' : '/api/docs' ,
561
+ } ) ;
562
+ } ) ;
563
+
564
+ it ( 'handles URL with auth credentials' , ( ) => {
565
+ const urlObject = parseStringToURLObject ( 'https://user:[email protected] /api/users' ) ! ;
566
+ const [ name , attributes ] = getHttpSpanDetailsFromUrlObject ( urlObject , 'test-origin' ) ;
567
+ expect ( name ) . toBe ( 'GET https://%filtered%:%filtered%@example.com/api/users' ) ;
568
+ expect ( attributes ) . toEqual ( {
569
+ 'sentry.origin' : 'test-origin' ,
570
+ 'sentry.source' : 'url' ,
571
+ 'url.path' : '/api/users' ,
572
+ 'url.full' :
'https://user:[email protected] /api/users' ,
573
+ 'server.address' : 'example.com' ,
574
+ 'url.scheme' : 'https:' ,
575
+ } ) ;
576
+ } ) ;
577
+
578
+ it ( 'handles URL with IPv4 address' , ( ) => {
579
+ const urlObject = parseStringToURLObject ( 'https://192.168.1.1:8080/api/users' ) ! ;
580
+ const [ name , attributes ] = getHttpSpanDetailsFromUrlObject ( urlObject , 'test-origin' ) ;
581
+ expect ( name ) . toBe ( 'GET https://192.168.1.1:8080/api/users' ) ;
582
+ expect ( attributes ) . toEqual ( {
583
+ 'sentry.origin' : 'test-origin' ,
584
+ 'sentry.source' : 'url' ,
585
+ 'url.path' : '/api/users' ,
586
+ 'url.full' : 'https://192.168.1.1:8080/api/users' ,
587
+ 'server.address' : '192.168.1.1' ,
588
+ 'url.scheme' : 'https:' ,
589
+ 'url.port' : '8080' ,
590
+ } ) ;
591
+ } ) ;
592
+
593
+ it ( 'handles URL with IPv6 address' , ( ) => {
594
+ const urlObject = parseStringToURLObject ( 'https://[2001:db8::1]:8080/api/users' ) ! ;
595
+ const [ name , attributes ] = getHttpSpanDetailsFromUrlObject ( urlObject , 'test-origin' ) ;
596
+ expect ( name ) . toBe ( 'GET https://[2001:db8::1]:8080/api/users' ) ;
597
+ expect ( attributes ) . toEqual ( {
598
+ 'sentry.origin' : 'test-origin' ,
599
+ 'sentry.source' : 'url' ,
600
+ 'url.path' : '/api/users' ,
601
+ 'url.full' : 'https://[2001:db8::1]:8080/api/users' ,
602
+ 'server.address' : '[2001:db8::1]' ,
603
+ 'url.scheme' : 'https:' ,
604
+ 'url.port' : '8080' ,
605
+ } ) ;
606
+ } ) ;
607
+
608
+ it ( 'handles URL with subdomain' , ( ) => {
609
+ const urlObject = parseStringToURLObject ( 'https://api.example.com/users' ) ! ;
610
+ const [ name , attributes ] = getHttpSpanDetailsFromUrlObject ( urlObject , 'test-origin' ) ;
611
+ expect ( name ) . toBe ( 'GET https://api.example.com/users' ) ;
612
+ expect ( attributes ) . toEqual ( {
613
+ 'sentry.origin' : 'test-origin' ,
614
+ 'sentry.source' : 'url' ,
615
+ 'url.path' : '/users' ,
616
+ 'url.full' : 'https://api.example.com/users' ,
617
+ 'server.address' : 'api.example.com' ,
618
+ 'url.scheme' : 'https:' ,
619
+ } ) ;
620
+ } ) ;
621
+ } ) ;
0 commit comments