Skip to content

Commit 0aa0999

Browse files
committed
add more tests
1 parent 6c1d18f commit 0aa0999

File tree

1 file changed

+267
-0
lines changed

1 file changed

+267
-0
lines changed

packages/core/test/utils-hoist/url.test.ts

+267
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { describe, expect, it } from 'vitest';
22
import {
3+
getHttpSpanDetailsFromUrlObject,
34
getSanitizedUrlString,
45
getSanitizedUrlStringFromUrlObject,
56
isURLObjectRelative,
@@ -344,6 +345,48 @@ describe('getSanitizedUrlStringFromUrlObject', () => {
344345
['url with port 4433', 'http://172.31.12.144:4433/test', 'http://172.31.12.144:4433/test'],
345346
['url with port 443', 'http://172.31.12.144:443/test', 'http://172.31.12.144/test'],
346347
['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+
],
347390
])('returns a sanitized URL for a %s', (_, rawUrl: string, sanitizedURL: string) => {
348391
const urlObject = parseStringToURLObject(rawUrl);
349392
if (!urlObject) {
@@ -352,3 +395,227 @@ describe('getSanitizedUrlStringFromUrlObject', () => {
352395
expect(getSanitizedUrlStringFromUrlObject(urlObject)).toEqual(sanitizedURL);
353396
});
354397
});
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

Comments
 (0)