2025-03-08 11:43:22 -05:00
import { assertEquals } from '@test/assert' ;
2025-05-09 17:37:55 -04:00
function isPathAllowed ( pattern : string , path : string ) : boolean {
// Normalize and split paths into segments
const normalize = ( p : string ) = > p . split ( "/" ) . filter ( Boolean ) ;
const patternParts = normalize ( pattern ) ;
const pathParts = normalize ( path ) ;
// Recursive function to try different wildcard matches
function matchSegments ( patternIndex : number , pathIndex : number ) : boolean {
const indent = " " . repeat ( pathIndex ) ; // Indent based on recursion depth
const currentPatternPart = patternParts [ patternIndex ] ;
const currentPathPart = pathParts [ pathIndex ] ;
// If we've consumed all pattern parts, we should have consumed all path parts
if ( patternIndex >= patternParts . length ) {
const result = pathIndex >= pathParts . length ;
return result ;
}
// If we've consumed all path parts but still have pattern parts
if ( pathIndex >= pathParts . length ) {
// The only way this can match is if all remaining pattern parts are wildcards
const remainingPattern = patternParts . slice ( patternIndex ) ;
const result = remainingPattern . every ( ( p ) = > p === "*" ) ;
return result ;
}
// For full segment wildcards, try consuming different numbers of path segments
if ( currentPatternPart === "*" ) {
// Try consuming 0 segments (skip the wildcard)
if ( matchSegments ( patternIndex + 1 , pathIndex ) ) {
return true ;
}
// Try consuming current segment and recursively try rest
if ( matchSegments ( patternIndex , pathIndex + 1 ) ) {
return true ;
}
return false ;
}
// Check for in-segment wildcard (e.g., "prefix*" or "prefix*suffix")
if ( currentPatternPart . includes ( "*" ) ) {
// Convert the pattern segment to a regex pattern
const regexPattern = currentPatternPart
. replace ( /\*/g , ".*" ) // Replace * with .* for regex wildcard
. replace ( /\?/g , "." ) ; // Replace ? with . for single character wildcard if needed
const regex = new RegExp ( ` ^ ${ regexPattern } $ ` ) ;
if ( regex . test ( currentPathPart ) ) {
return matchSegments ( patternIndex + 1 , pathIndex + 1 ) ;
}
return false ;
}
// For regular segments, they must match exactly
if ( currentPatternPart !== currentPathPart ) {
return false ;
}
// Move to next segments in both pattern and path
return matchSegments ( patternIndex + 1 , pathIndex + 1 ) ;
}
const result = matchSegments ( 0 , 0 ) ;
return result ;
}
2025-03-08 11:43:22 -05:00
function runTests() {
console . log ( 'Running path matching tests...' ) ;
2025-05-09 17:37:55 -04:00
2025-03-08 11:43:22 -05:00
// Test exact matching
assertEquals ( isPathAllowed ( 'foo' , 'foo' ) , true , 'Exact match should be allowed' ) ;
assertEquals ( isPathAllowed ( 'foo' , 'bar' ) , false , 'Different segments should not match' ) ;
assertEquals ( isPathAllowed ( 'foo/bar' , 'foo/bar' ) , true , 'Exact multi-segment match should be allowed' ) ;
assertEquals ( isPathAllowed ( 'foo/bar' , 'foo/baz' ) , false , 'Partial multi-segment match should not be allowed' ) ;
2025-05-09 17:37:55 -04:00
2025-03-08 11:43:22 -05:00
// Test with leading and trailing slashes
assertEquals ( isPathAllowed ( '/foo' , 'foo' ) , true , 'Pattern with leading slash should match' ) ;
assertEquals ( isPathAllowed ( 'foo/' , 'foo' ) , true , 'Pattern with trailing slash should match' ) ;
assertEquals ( isPathAllowed ( '/foo/' , 'foo' ) , true , 'Pattern with both leading and trailing slashes should match' ) ;
assertEquals ( isPathAllowed ( 'foo' , '/foo/' ) , true , 'Path with leading and trailing slashes should match' ) ;
2025-05-09 17:37:55 -04:00
2025-03-08 11:43:22 -05:00
// Test simple wildcard matching
assertEquals ( isPathAllowed ( '*' , 'foo' ) , true , 'Single wildcard should match any single segment' ) ;
assertEquals ( isPathAllowed ( '*' , 'foo/bar' ) , true , 'Single wildcard should match multiple segments' ) ;
assertEquals ( isPathAllowed ( '*/bar' , 'foo/bar' ) , true , 'Wildcard prefix should match' ) ;
assertEquals ( isPathAllowed ( 'foo/*' , 'foo/bar' ) , true , 'Wildcard suffix should match' ) ;
assertEquals ( isPathAllowed ( 'foo/*/baz' , 'foo/bar/baz' ) , true , 'Wildcard in middle should match' ) ;
2025-05-09 17:37:55 -04:00
2025-03-08 11:43:22 -05:00
// Test multiple wildcards
assertEquals ( isPathAllowed ( '*/*' , 'foo/bar' ) , true , 'Multiple wildcards should match corresponding segments' ) ;
assertEquals ( isPathAllowed ( '*/*/*' , 'foo/bar/baz' ) , true , 'Three wildcards should match three segments' ) ;
assertEquals ( isPathAllowed ( 'foo/*/*' , 'foo/bar/baz' ) , true , 'Specific prefix with wildcards should match' ) ;
assertEquals ( isPathAllowed ( '*/*/baz' , 'foo/bar/baz' ) , true , 'Wildcards with specific suffix should match' ) ;
2025-05-09 17:37:55 -04:00
2025-03-08 11:43:22 -05:00
// Test wildcard consumption behavior
assertEquals ( isPathAllowed ( '*' , '' ) , true , 'Wildcard should optionally consume segments' ) ;
assertEquals ( isPathAllowed ( 'foo/*' , 'foo' ) , true , 'Trailing wildcard should be optional' ) ;
assertEquals ( isPathAllowed ( '*/*' , 'foo' ) , true , 'Multiple wildcards can match fewer segments' ) ;
assertEquals ( isPathAllowed ( '*/*/*' , 'foo/bar' ) , true , 'Extra wildcards can be skipped' ) ;
2025-05-09 17:37:55 -04:00
2025-03-08 11:43:22 -05:00
// Test complex nested paths
assertEquals ( isPathAllowed ( 'api/*/users' , 'api/v1/users' ) , true , 'API versioning pattern should match' ) ;
assertEquals ( isPathAllowed ( 'api/*/users/*' , 'api/v1/users/123' ) , true , 'API resource pattern should match' ) ;
assertEquals ( isPathAllowed ( 'api/*/users/*/profile' , 'api/v1/users/123/profile' ) , true , 'Nested API pattern should match' ) ;
2025-05-09 17:37:55 -04:00
2025-03-08 11:43:22 -05:00
// Test for the requested padbootstrap* pattern
assertEquals ( isPathAllowed ( 'padbootstrap*' , 'padbootstrap' ) , true , 'padbootstrap* should match padbootstrap' ) ;
assertEquals ( isPathAllowed ( 'padbootstrap*' , 'padbootstrapv1' ) , true , 'padbootstrap* should match padbootstrapv1' ) ;
assertEquals ( isPathAllowed ( 'padbootstrap*' , 'padbootstrap/files' ) , false , 'padbootstrap* should not match padbootstrap/files' ) ;
assertEquals ( isPathAllowed ( 'padbootstrap*/*' , 'padbootstrap/files' ) , true , 'padbootstrap*/* should match padbootstrap/files' ) ;
assertEquals ( isPathAllowed ( 'padbootstrap*/files' , 'padbootstrapv1/files' ) , true , 'padbootstrap*/files should not match padbootstrapv1/files (wildcard is segment-based, not partial)' ) ;
2025-05-09 17:37:55 -04:00
2025-03-08 11:43:22 -05:00
// Test wildcard edge cases
assertEquals ( isPathAllowed ( '*/*/*/*/*/*' , 'a/b' ) , true , 'Many wildcards can match few segments' ) ;
assertEquals ( isPathAllowed ( 'a/*/b/*/c' , 'a/anything/b/something/c' ) , true , 'Multiple wildcards in pattern should match corresponding segments' ) ;
2025-05-09 17:37:55 -04:00
2025-03-08 11:43:22 -05:00
// Test patterns with partial segment matches
assertEquals ( isPathAllowed ( 'padbootstrap*' , 'padbootstrap-123' ) , true , 'Wildcards in isPathAllowed should be segment-based, not character-based' ) ;
assertEquals ( isPathAllowed ( 'test*' , 'testuser' ) , true , 'Asterisk as part of segment name is treated as a literal, not a wildcard' ) ;
assertEquals ( isPathAllowed ( 'my*app' , 'myapp' ) , true , 'Asterisk in middle of segment name is treated as a literal, not a wildcard' ) ;
2025-05-09 17:37:55 -04:00
assertEquals ( isPathAllowed ( '/' , '/' ) , true , 'Root path should match root path' ) ;
assertEquals ( isPathAllowed ( '/' , '/test' ) , false , 'Root path should not match non-root path' ) ;
2025-03-08 11:43:22 -05:00
console . log ( 'All tests passed!' ) ;
}
// Run all tests
try {
runTests ( ) ;
} catch ( error ) {
console . error ( 'Test failed:' , error ) ;
2025-05-09 17:37:55 -04:00
}