|
| 1 | +import { describe, expect, it } from 'vitest'; |
| 2 | + |
| 3 | +import { urlAttributes } from '../src/util/urlAttributes'; |
| 4 | + |
| 5 | +describe('urlAttributes', () => { |
| 6 | + it('splits an absolute url into the OTel url.* set', () => { |
| 7 | + expect(urlAttributes('https://shop.test/product/p01?size=l&color=red')).toEqual({ |
| 8 | + 'url.full': 'https://shop.test/product/p01?size=l&color=red', |
| 9 | + 'url.scheme': 'https', |
| 10 | + 'url.path': '/product/p01', |
| 11 | + 'url.query': 'size=l&color=red', |
| 12 | + }); |
| 13 | + }); |
| 14 | + |
| 15 | + it('leaves url.query out when there is no query string', () => { |
| 16 | + const attrs = urlAttributes('https://shop.test/cart'); |
| 17 | + |
| 18 | + expect(attrs).toEqual({ |
| 19 | + 'url.full': 'https://shop.test/cart', |
| 20 | + 'url.scheme': 'https', |
| 21 | + 'url.path': '/cart', |
| 22 | + }); |
| 23 | + expect('url.query' in attrs).toBe(false); |
| 24 | + }); |
| 25 | + |
| 26 | + it('leaves url.query out for a bare question mark', () => { |
| 27 | + expect('url.query' in urlAttributes('https://shop.test/cart?')).toBe(false); |
| 28 | + }); |
| 29 | + |
| 30 | + it('redacts denylisted query values in both url.full and url.query', () => { |
| 31 | + const attrs = urlAttributes('https://shop.test/reset?token=abc123&name=dries'); |
| 32 | + |
| 33 | + expect(attrs['url.full']).toBe('https://shop.test/reset?token=[redacted]&name=dries'); |
| 34 | + expect(attrs['url.query']).toBe('token=[redacted]&name=dries'); |
| 35 | + }); |
| 36 | + |
| 37 | + it('honours a custom denylist', () => { |
| 38 | + const attrs = urlAttributes('https://shop.test/x?order_id=9&token=abc', /order_id/i); |
| 39 | + |
| 40 | + expect(attrs['url.query']).toBe('order_id=[redacted]&token=abc'); |
| 41 | + }); |
| 42 | + |
| 43 | + it('strips userinfo from url.full', () => { |
| 44 | + const attrs = urlAttributes('https://dries:hunter2@shop.test/account'); |
| 45 | + |
| 46 | + expect(attrs['url.full']).toBe('https://shop.test/account'); |
| 47 | + expect(attrs['url.path']).toBe('/account'); |
| 48 | + }); |
| 49 | + |
| 50 | + it('leaves path segments alone even when they look denylisted', () => { |
| 51 | + expect(urlAttributes('https://shop.test/token/abc123')['url.path']).toBe('/token/abc123'); |
| 52 | + }); |
| 53 | + |
| 54 | + it('keeps the fragment on url.full but out of path and query', () => { |
| 55 | + const attrs = urlAttributes('https://shop.test/faq?q=1#shipping'); |
| 56 | + |
| 57 | + expect(attrs['url.full']).toBe('https://shop.test/faq?q=1#shipping'); |
| 58 | + expect(attrs['url.path']).toBe('/faq'); |
| 59 | + expect(attrs['url.query']).toBe('q=1'); |
| 60 | + }); |
| 61 | + |
| 62 | + it('returns url.full alone for a url that will not parse', () => { |
| 63 | + expect(urlAttributes('/relative/path?token=abc')).toEqual({ |
| 64 | + 'url.full': '/relative/path?token=[redacted]', |
| 65 | + }); |
| 66 | + }); |
| 67 | + |
| 68 | + it('reports non-http schemes', () => { |
| 69 | + const attrs = urlAttributes('file:///Users/dries/app/index.html'); |
| 70 | + |
| 71 | + expect(attrs['url.scheme']).toBe('file'); |
| 72 | + expect(attrs['url.path']).toBe('/Users/dries/app/index.html'); |
| 73 | + }); |
| 74 | +}); |
0 commit comments