import { tap, expect } from '@git.zone/tstest/tapbundle'; import { PathMatcher } from '../../../ts/core/routing/matchers/path.js'; tap.test('PathMatcher - exact match', async () => { const result = PathMatcher.match('/api/users', '/api/users'); expect(result.matches).toEqual(true); expect(result.pathMatch).toEqual('/api/users'); expect(result.pathRemainder).toEqual(''); expect(result.params).toEqual({}); }); tap.test('PathMatcher - no match', async () => { const result = PathMatcher.match('/api/users', '/api/posts'); expect(result.matches).toEqual(false); }); tap.test('PathMatcher - parameter extraction', async () => { const result = PathMatcher.match('/users/:id/profile', '/users/123/profile'); expect(result.matches).toEqual(true); expect(result.params).toEqual({ id: '123' }); expect(result.pathMatch).toEqual('/users/123/profile'); expect(result.pathRemainder).toEqual(''); }); tap.test('PathMatcher - multiple parameters', async () => { const result = PathMatcher.match('/api/:version/users/:id', '/api/v2/users/456'); expect(result.matches).toEqual(true); expect(result.params).toEqual({ version: 'v2', id: '456' }); }); tap.test('PathMatcher - wildcard matching', async () => { const result = PathMatcher.match('/api/*', '/api/users/123/profile'); expect(result.matches).toEqual(true); expect(result.pathMatch).toEqual('/api'); // Normalized without trailing slash expect(result.pathRemainder).toEqual('users/123/profile'); }); tap.test('PathMatcher - mixed parameters and wildcards', async () => { const result = PathMatcher.match('/api/:version/*', '/api/v1/users/123'); expect(result.matches).toEqual(true); expect(result.params).toEqual({ version: 'v1' }); expect(result.pathRemainder).toEqual('users/123'); }); tap.test('PathMatcher - trailing slash normalization', async () => { // Both with trailing slash let result = PathMatcher.match('/api/users/', '/api/users/'); expect(result.matches).toEqual(true); // Pattern with, path without result = PathMatcher.match('/api/users/', '/api/users'); expect(result.matches).toEqual(true); // Pattern without, path with result = PathMatcher.match('/api/users', '/api/users/'); expect(result.matches).toEqual(true); }); tap.test('PathMatcher - root path handling', async () => { const result = PathMatcher.match('/', '/'); expect(result.matches).toEqual(true); expect(result.pathMatch).toEqual('/'); expect(result.pathRemainder).toEqual(''); }); tap.test('PathMatcher - specificity calculation', async () => { // Exact paths are most specific const exactScore = PathMatcher.calculateSpecificity('/api/v1/users'); const paramScore = PathMatcher.calculateSpecificity('/api/:version/users'); const wildcardScore = PathMatcher.calculateSpecificity('/api/*'); expect(exactScore).toBeGreaterThan(paramScore); expect(paramScore).toBeGreaterThan(wildcardScore); // More segments = more specific const deepPath = PathMatcher.calculateSpecificity('/api/v1/users/profile/settings'); const shallowPath = PathMatcher.calculateSpecificity('/api/users'); expect(deepPath).toBeGreaterThan(shallowPath); // More static segments = more specific const moreStatic = PathMatcher.calculateSpecificity('/api/v1/users/:id'); const lessStatic = PathMatcher.calculateSpecificity('/api/:version/:resource/:id'); expect(moreStatic).toBeGreaterThan(lessStatic); }); tap.test('PathMatcher - findAllMatches', async () => { const patterns = [ '/api/users', '/api/users/:id', '/api/users/:id/profile', '/api/*', '/*' ]; const matches = PathMatcher.findAllMatches(patterns, '/api/users/123/profile'); // With the stricter path matching, /api/users won't match /api/users/123/profile // Only patterns with wildcards, parameters, or exact matches will work expect(matches).toHaveLength(4); // Verify all expected patterns are in the results const matchedPatterns = matches.map(m => m.pattern); expect(matchedPatterns).not.toContain('/api/users'); // This won't match anymore (no prefix matching) expect(matchedPatterns).toContain('/api/users/:id'); expect(matchedPatterns).toContain('/api/users/:id/profile'); expect(matchedPatterns).toContain('/api/*'); expect(matchedPatterns).toContain('/*'); // Verify parameters were extracted correctly for parameterized patterns const paramsById = matches.find(m => m.pattern === '/api/users/:id'); const paramsByIdProfile = matches.find(m => m.pattern === '/api/users/:id/profile'); expect(paramsById?.result.params).toEqual({ id: '123' }); expect(paramsByIdProfile?.result.params).toEqual({ id: '123' }); }); tap.test('PathMatcher - edge cases', async () => { // Empty patterns expect(PathMatcher.match('', '/api/users').matches).toEqual(false); expect(PathMatcher.match('/api/users', '').matches).toEqual(false); expect(PathMatcher.match('', '').matches).toEqual(false); // Null/undefined expect(PathMatcher.match(null as any, '/api/users').matches).toEqual(false); expect(PathMatcher.match('/api/users', null as any).matches).toEqual(false); }); tap.start();