diff --git a/server/lib/ip.test.ts b/server/lib/ip.test.ts index f3925cf..2c2dd05 100644 --- a/server/lib/ip.test.ts +++ b/server/lib/ip.test.ts @@ -1,61 +1,5 @@ import { cidrToRange, findNextAvailableCidr } from "./ip"; - -/** - * Compares two objects for deep equality - * @param actual The actual value to test - * @param expected The expected value to compare against - * @param message The message to display if assertion fails - * @throws Error if objects are not equal - */ -export function assertEqualsObj(actual: T, expected: T, message: string): void { - const actualStr = JSON.stringify(actual); - const expectedStr = JSON.stringify(expected); - if (actualStr !== expectedStr) { - throw new Error(`${message}\nExpected: ${expectedStr}\nActual: ${actualStr}`); - } -} - -/** - * Compares two primitive values for equality - * @param actual The actual value to test - * @param expected The expected value to compare against - * @param message The message to display if assertion fails - * @throws Error if values are not equal - */ -export function assertEquals(actual: T, expected: T, message: string): void { - if (actual !== expected) { - throw new Error(`${message}\nExpected: ${expected}\nActual: ${actual}`); - } -} - -/** - * Tests if a function throws an expected error - * @param fn The function to test - * @param expectedError The expected error message or part of it - * @param message The message to display if assertion fails - * @throws Error if function doesn't throw or throws unexpected error - */ -export function assertThrows( - fn: () => void, - expectedError: string, - message: string -): void { - try { - fn(); - throw new Error(`${message}: Expected to throw "${expectedError}"`); - } catch (error) { - if (!(error instanceof Error)) { - throw new Error(`${message}\nUnexpected error type: ${typeof error}`); - } - - if (!error.message.includes(expectedError)) { - throw new Error( - `${message}\nExpected error: ${expectedError}\nActual error: ${error.message}` - ); - } - } -} - +import { assertEquals } from "@test/assert"; // Test cases function testFindNextAvailableCidr() { diff --git a/server/lib/validators.test.ts b/server/lib/validators.test.ts new file mode 100644 index 0000000..e2043c7 --- /dev/null +++ b/server/lib/validators.test.ts @@ -0,0 +1,71 @@ +import { isValidUrlGlobPattern } from "./validators"; +import { assertEquals } from "@test/assert"; + +function runTests() { + console.log('Running URL pattern validation tests...'); + + // Test valid patterns + assertEquals(isValidUrlGlobPattern('simple'), true, 'Simple path segment should be valid'); + assertEquals(isValidUrlGlobPattern('simple/path'), true, 'Simple path with slash should be valid'); + assertEquals(isValidUrlGlobPattern('/leading/slash'), true, 'Path with leading slash should be valid'); + assertEquals(isValidUrlGlobPattern('path/'), true, 'Path with trailing slash should be valid'); + assertEquals(isValidUrlGlobPattern('path/*'), true, 'Path with wildcard segment should be valid'); + assertEquals(isValidUrlGlobPattern('*'), true, 'Single wildcard should be valid'); + assertEquals(isValidUrlGlobPattern('*/subpath'), true, 'Wildcard with subpath should be valid'); + assertEquals(isValidUrlGlobPattern('path/*/more'), true, 'Path with wildcard in the middle should be valid'); + + // Test with special characters + assertEquals(isValidUrlGlobPattern('path-with-dash'), true, 'Path with dash should be valid'); + assertEquals(isValidUrlGlobPattern('path_with_underscore'), true, 'Path with underscore should be valid'); + assertEquals(isValidUrlGlobPattern('path.with.dots'), true, 'Path with dots should be valid'); + assertEquals(isValidUrlGlobPattern('path~with~tilde'), true, 'Path with tilde should be valid'); + assertEquals(isValidUrlGlobPattern('path!with!exclamation'), true, 'Path with exclamation should be valid'); + assertEquals(isValidUrlGlobPattern('path$with$dollar'), true, 'Path with dollar should be valid'); + assertEquals(isValidUrlGlobPattern('path&with&ersand'), true, 'Path with ampersand should be valid'); + assertEquals(isValidUrlGlobPattern("path'with'quote"), true, "Path with quote should be valid"); + assertEquals(isValidUrlGlobPattern('path(with)parentheses'), true, 'Path with parentheses should be valid'); + assertEquals(isValidUrlGlobPattern('path+with+plus'), true, 'Path with plus should be valid'); + assertEquals(isValidUrlGlobPattern('path,with,comma'), true, 'Path with comma should be valid'); + assertEquals(isValidUrlGlobPattern('path;with;semicolon'), true, 'Path with semicolon should be valid'); + assertEquals(isValidUrlGlobPattern('path=with=equals'), true, 'Path with equals should be valid'); + assertEquals(isValidUrlGlobPattern('path:with:colon'), true, 'Path with colon should be valid'); + assertEquals(isValidUrlGlobPattern('path@with@at'), true, 'Path with at should be valid'); + + // Test with percent encoding + assertEquals(isValidUrlGlobPattern('path%20with%20spaces'), true, 'Path with percent-encoded spaces should be valid'); + assertEquals(isValidUrlGlobPattern('path%2Fwith%2Fencoded%2Fslashes'), true, 'Path with percent-encoded slashes should be valid'); + + // Test with wildcards in segments (the fixed functionality) + assertEquals(isValidUrlGlobPattern('padbootstrap*'), true, 'Path with wildcard at the end of segment should be valid'); + assertEquals(isValidUrlGlobPattern('pad*bootstrap'), true, 'Path with wildcard in the middle of segment should be valid'); + assertEquals(isValidUrlGlobPattern('*bootstrap'), true, 'Path with wildcard at the start of segment should be valid'); + assertEquals(isValidUrlGlobPattern('multiple*wildcards*in*segment'), true, 'Path with multiple wildcards in segment should be valid'); + assertEquals(isValidUrlGlobPattern('wild*/cards/in*/different/seg*ments'), true, 'Path with wildcards in different segments should be valid'); + + // Test invalid patterns + assertEquals(isValidUrlGlobPattern(''), false, 'Empty string should be invalid'); + assertEquals(isValidUrlGlobPattern('//double/slash'), false, 'Path with double slash should be invalid'); + assertEquals(isValidUrlGlobPattern('path//end'), false, 'Path with double slash in the middle should be invalid'); + assertEquals(isValidUrlGlobPattern('invalid'), false, 'Path with invalid characters should be invalid'); + assertEquals(isValidUrlGlobPattern('invalid|char'), false, 'Path with invalid pipe character should be invalid'); + assertEquals(isValidUrlGlobPattern('invalid"char'), false, 'Path with invalid quote character should be invalid'); + assertEquals(isValidUrlGlobPattern('invalid`char'), false, 'Path with invalid backtick character should be invalid'); + assertEquals(isValidUrlGlobPattern('invalid^char'), false, 'Path with invalid caret character should be invalid'); + assertEquals(isValidUrlGlobPattern('invalid\\char'), false, 'Path with invalid backslash character should be invalid'); + assertEquals(isValidUrlGlobPattern('invalid[char]'), false, 'Path with invalid square brackets should be invalid'); + assertEquals(isValidUrlGlobPattern('invalid{char}'), false, 'Path with invalid curly braces should be invalid'); + + // Test invalid percent encoding + assertEquals(isValidUrlGlobPattern('invalid%2'), false, 'Path with incomplete percent encoding should be invalid'); + assertEquals(isValidUrlGlobPattern('invalid%GZ'), false, 'Path with invalid hex in percent encoding should be invalid'); + assertEquals(isValidUrlGlobPattern('invalid%'), false, 'Path with isolated percent sign should be invalid'); + + console.log('All tests passed!'); +} + +// Run all tests +try { + runTests(); +} catch (error) { + console.error('Test failed:', error); +} \ No newline at end of file diff --git a/server/routers/badger/verifySession.test.ts b/server/routers/badger/verifySession.test.ts new file mode 100644 index 0000000..0a459dc --- /dev/null +++ b/server/routers/badger/verifySession.test.ts @@ -0,0 +1,67 @@ +import { isPathAllowed } from './verifySession'; +import { assertEquals } from '@test/assert'; + +function runTests() { + console.log('Running path matching tests...'); + + // 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'); + + // 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'); + + // 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'); + + // 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'); + + // 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'); + + // 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'); + + // 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)'); + + // 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'); + + // 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'); + + console.log('All tests passed!'); +} + +// Run all tests +try { + runTests(); +} catch (error) { + console.error('Test failed:', error); +} \ No newline at end of file diff --git a/test/assert.ts b/test/assert.ts new file mode 100644 index 0000000..44b6f92 --- /dev/null +++ b/test/assert.ts @@ -0,0 +1,55 @@ +/** + * Compares two objects for deep equality + * @param actual The actual value to test + * @param expected The expected value to compare against + * @param message The message to display if assertion fails + * @throws Error if objects are not equal + */ +export function assertEqualsObj(actual: T, expected: T, message: string): void { + const actualStr = JSON.stringify(actual); + const expectedStr = JSON.stringify(expected); + if (actualStr !== expectedStr) { + throw new Error(`${message}\nExpected: ${expectedStr}\nActual: ${actualStr}`); + } +} + +/** + * Compares two primitive values for equality + * @param actual The actual value to test + * @param expected The expected value to compare against + * @param message The message to display if assertion fails + * @throws Error if values are not equal + */ +export function assertEquals(actual: T, expected: T, message: string): void { + if (actual !== expected) { + throw new Error(`${message}\nExpected: ${expected}\nActual: ${actual}`); + } +} + +/** + * Tests if a function throws an expected error + * @param fn The function to test + * @param expectedError The expected error message or part of it + * @param message The message to display if assertion fails + * @throws Error if function doesn't throw or throws unexpected error + */ +export function assertThrows( + fn: () => void, + expectedError: string, + message: string +): void { + try { + fn(); + throw new Error(`${message}: Expected to throw "${expectedError}"`); + } catch (error) { + if (!(error instanceof Error)) { + throw new Error(`${message}\nUnexpected error type: ${typeof error}`); + } + + if (!error.message.includes(expectedError)) { + throw new Error( + `${message}\nExpected error: ${expectedError}\nActual error: ${error.message}` + ); + } + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index d92e727..9472939 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,6 +15,7 @@ "baseUrl": "src", "paths": { "@server/*": ["../server/*"], + "@test/*": ["../test/*"], "@app/*": ["*"], "@/*": ["./*"] },