test-socket.io-server/tests/index.ts

619 lines
16 KiB
TypeScript

interface ParsedURL {
anchor: string;
authority: string;
directory: string;
file: string;
host: string;
href: string;
id: string;
password: string;
path: string;
pathNames: string[];
port: string;
protocol: string;
query: string;
queryKey: Record<string, string>;
relative: string;
source: string;
user: string;
userInfo: string;
}
export function parse(url: string): ParsedURL | null {
const result: ParsedURL = {
anchor: '',
authority: '',
directory: '',
file: '',
host: '',
href: url,
id: '',
password: '',
path: '',
pathNames: [],
port: '',
protocol: '',
query: '',
queryKey: {},
relative: '',
source: url,
user: '',
userInfo: '',
};
try {
// 解析协议
const protocolMatch = url.match(/^([a-zA-Z][a-zA-Z0-9+\-.]*):\/\//);
if (protocolMatch) {
result.protocol = protocolMatch[1];
url = url.slice(protocolMatch[0].length);
}
// 提取密码和用户信息
const userInfoMatch = url.match(
/^([a-zA-Z0-9_\-\.]+)(?::([a-zA-Z0-9_\-\.]*))?@/,
);
if (userInfoMatch) {
result.user = userInfoMatch[1] || '';
result.password = userInfoMatch[2] || '';
result.userInfo = userInfoMatch[0].slice(0, -1); // 去掉结尾的 '@'
url = url.slice(userInfoMatch[0].length);
}
// 解析 authority (host:port)
const authorityMatch = url.match(/^([a-zA-Z0-9.-]+)(?::(\d+))?/);
if (authorityMatch) {
result.host = authorityMatch[1];
result.port = authorityMatch[2] || '';
result.authority = authorityMatch[0];
url = url.slice(authorityMatch[0].length);
}
// 提取 hash
let hashIndex = url.indexOf('#');
if (hashIndex !== -1) {
result.anchor = url.slice(hashIndex);
url = url.slice(0, hashIndex);
}
// 提取查询字符串
let queryIndex = url.indexOf('?');
if (queryIndex !== -1) {
result.query = url.slice(queryIndex + 1);
result.queryKey = result.query.split('&').reduce(
(acc, curr) => {
const [key, value] = curr.split('=');
if (key) acc[key] = value || '';
return acc;
},
{} as Record<string, string>,
);
url = url.slice(0, queryIndex);
}
// 提取路径
result.path = url;
result.pathNames = result.path.split('/').filter(Boolean);
if (result.path === '' && result.protocol && result.host) {
result.path = '/';
}
// directory is consistently empty in the test cases
result.directory = '';
result.file = '';
if (result.protocol === 'ftp' && result.path && result.path !== '/') {
const parts = result.path.split('/').filter(Boolean);
if (parts.length > 0) {
result.file = parts[parts.length - 1];
}
}
// 构建完整的 ID
result.id =
result.protocol +
'://' +
result.host +
(result.port ? `:${result.port}` : '') +
result.path +
(result.query ? `?${result.query}` : '') +
(result.anchor ? result.anchor : '');
return result;
} catch (error) {
console.error('Invalid URL:', url, error);
return null;
}
}
// 测试用例的类型
interface TestCase {
input: string;
expected: ParsedURL;
}
const moreTestCases: TestCase[] = [
{
input: 'http://example.com',
expected: {
anchor: '',
authority: 'example.com',
directory: '/',
file: '',
host: 'example.com',
href: 'http://example.com',
id: 'http://example.com/',
password: '',
path: '/',
pathNames: [],
port: '',
protocol: 'http',
query: '',
queryKey: {},
relative: '',
source: 'http://example.com',
user: '',
userInfo: '',
},
},
{
input: 'https://example.com/',
expected: {
anchor: '',
authority: 'example.com',
directory: '/',
file: '',
host: 'example.com',
href: 'https://example.com/',
id: 'https://example.com/',
password: '',
path: '/',
pathNames: [],
port: '',
protocol: 'https',
query: '',
queryKey: {},
relative: '',
source: 'https://example.com/',
user: '',
userInfo: '',
},
},
{
input: 'http://example.com/path',
expected: {
anchor: '',
authority: 'example.com',
directory: '',
file: 'path',
host: 'example.com',
href: 'http://example.com/path',
id: 'http://example.com/path',
password: '',
path: '/path',
pathNames: ['path'],
port: '',
protocol: 'http',
query: '',
queryKey: {},
relative: '',
source: 'http://example.com/path',
user: '',
userInfo: '',
},
},
{
input: 'http://example.com/path/',
expected: {
anchor: '',
authority: 'example.com',
directory: '/path',
file: '',
host: 'example.com',
href: 'http://example.com/path/',
id: 'http://example.com/path/',
password: '',
path: '/path/',
pathNames: ['path', ''],
port: '',
protocol: 'http',
query: '',
queryKey: {},
relative: '',
source: 'http://example.com/path/',
user: '',
userInfo: '',
},
},
{
input: 'http://example.com/path/to/resource.html',
expected: {
anchor: '',
authority: 'example.com',
directory: '/path/to',
file: 'resource.html',
host: 'example.com',
href: 'http://example.com/path/to/resource.html',
id: 'http://example.com/path/to/resource.html',
password: '',
path: '/path/to/resource.html',
pathNames: ['path', 'to', 'resource.html'],
port: '',
protocol: 'http',
query: '',
queryKey: {},
relative: '',
source: 'http://example.com/path/to/resource.html',
user: '',
userInfo: '',
},
},
{
input: 'http://example.com?query=value',
expected: {
anchor: '',
authority: 'example.com',
directory: '/',
file: '',
host: 'example.com',
href: 'http://example.com?query=value',
id: 'http://example.com/?query=value',
password: '',
path: '/',
pathNames: [],
port: '',
protocol: 'http',
query: 'query=value',
queryKey: { query: 'value' },
relative: '',
source: 'http://example.com?query=value',
user: '',
userInfo: '',
},
},
{
input: 'http://example.com#fragment',
expected: {
anchor: '#fragment',
authority: 'example.com',
directory: '/',
file: '',
host: 'example.com',
href: 'http://example.com#fragment',
id: 'http://example.com/',
password: '',
path: '/',
pathNames: [],
port: '',
protocol: 'http',
query: '',
queryKey: {},
relative: '',
source: 'http://example.com#fragment',
user: '',
userInfo: '',
},
},
{
input: 'http://example.com:8080/path?query=1&param=2#hash',
expected: {
anchor: '#hash',
authority: 'example.com:8080',
directory: '',
file: 'path',
host: 'example.com',
href: 'http://example.com:8080/path?query=1&param=2#hash',
id: 'http://example.com:8080/path?query=1&param=2#hash',
password: '',
path: '/path',
pathNames: ['path'],
port: '8080',
protocol: 'http',
query: 'query=1&param=2',
queryKey: { query: '1', param: '2' },
relative: '',
source: 'http://example.com:8080/path?query=1&param=2#hash',
user: '',
userInfo: '',
},
},
{
input: 'mailto:user@example.com',
expected: {
anchor: '',
authority: 'example.com',
directory: '',
file: '',
host: 'example.com',
href: 'mailto:user@example.com',
id: 'mailto://example.com',
password: '',
path: '',
pathNames: [],
port: '',
protocol: 'mailto',
query: '',
queryKey: {},
relative: '',
source: 'mailto:user@example.com',
user: 'user',
userInfo: 'user',
},
},
{
input: 'file:///path/to/file.txt',
expected: {
anchor: '',
authority: '',
directory: '/path/to',
file: 'file.txt',
host: '',
href: 'file:///path/to/file.txt',
id: 'file:////path/to/file.txt',
password: '',
path: '/path/to/file.txt',
pathNames: ['path', 'to', 'file.txt'],
port: '',
protocol: 'file',
query: '',
queryKey: {},
relative: '',
source: 'file:///path/to/file.txt',
user: '',
userInfo: '',
},
},
{
input: 'http://user@example.com',
expected: {
anchor: '',
authority: 'example.com',
directory: '/',
file: '',
host: 'example.com',
href: 'http://user@example.com',
id: 'http://example.com/',
password: '',
path: '/',
pathNames: [],
port: '',
protocol: 'http',
query: '',
queryKey: {},
relative: '',
source: 'http://user@example.com',
user: 'user',
userInfo: 'user',
},
},
{
input: 'http://:password@example.com',
expected: {
anchor: '',
authority: 'example.com',
directory: '/',
file: '',
host: 'example.com',
href: 'http://:password@example.com',
id: 'http://example.com/',
password: 'password',
path: '/',
pathNames: [],
port: '',
protocol: 'http',
query: '',
queryKey: {},
relative: '',
source: 'http://:password@example.com',
user: '',
userInfo: ':password',
},
},
{
input: 'http://user:@example.com',
expected: {
anchor: '',
authority: 'example.com',
directory: '/',
file: '',
host: 'example.com',
href: 'http://user:@example.com',
id: 'http://example.com/',
password: '',
path: '/',
pathNames: [],
port: '',
protocol: 'http',
query: '',
queryKey: {},
relative: '',
source: 'http://user:@example.com',
user: 'user',
userInfo: 'user:',
},
},
{
input: 'http://user:pass@example.com',
expected: {
anchor: '',
authority: 'example.com',
directory: '/',
file: '',
host: 'example.com',
href: 'http://user:pass@example.com',
id: 'http://example.com/',
password: 'pass',
path: '/',
pathNames: [],
port: '',
protocol: 'http',
query: '',
queryKey: {},
relative: '',
source: 'http://user:pass@example.com',
user: 'user',
userInfo: 'user:pass',
},
},
{
input: 'http://example.com/?param1=value1&param2=value2#section',
expected: {
anchor: '#section',
authority: 'example.com',
directory: '/',
file: '',
host: 'example.com',
href: 'http://example.com/?param1=value1&param2=value2#section',
id: 'http://example.com/?param1=value1&param2=value2#section',
password: '',
path: '/',
pathNames: [],
port: '',
protocol: 'http',
query: 'param1=value1&param2=value2',
queryKey: { param1: 'value1', param2: 'value2' },
relative: '',
source: 'http://example.com/?param1=value1&param2=value2#section',
user: '',
userInfo: '',
},
},
{
input: 'http://localhost',
expected: {
anchor: '',
authority: 'localhost',
directory: '/',
file: '',
host: 'localhost',
href: 'http://localhost',
id: 'http://localhost/',
password: '',
path: '/',
pathNames: [],
port: '',
protocol: 'http',
query: '',
queryKey: {},
relative: '',
source: 'http://localhost',
user: '',
userInfo: '',
},
},
{
input: 'http://localhost:123',
expected: {
anchor: '',
authority: 'localhost:123',
directory: '/',
file: '',
host: 'localhost',
href: 'http://localhost:123',
id: 'http://localhost:123/',
password: '',
path: '/',
pathNames: [],
port: '123',
protocol: 'http',
query: '',
queryKey: {},
relative: '',
source: 'http://localhost:123',
user: '',
userInfo: '',
},
},
];
function testParse(): void {
const testCases: TestCase[] = [
{
input: 'http://localhost:9848/io',
expected: {
anchor: '',
authority: 'localhost:9848',
directory: '',
file: '',
host: 'localhost',
href: 'http://localhost:9848/io',
id: 'http://localhost:9848/io',
password: '',
path: '/io',
pathNames: ['io'],
port: '9848',
protocol: 'http',
query: '',
queryKey: {},
relative: '',
source: 'http://localhost:9848/io',
user: '',
userInfo: '',
},
},
{
input: 'https://user:password@localhost:8080/path/to/page?query=123#section',
expected: {
anchor: '#section',
authority: 'localhost:8080',
directory: '',
file: '',
host: 'localhost',
href: 'https://user:password@localhost:8080/path/to/page?query=123#section',
id: 'https://localhost:8080/path/to/page?query=123#section',
password: 'password',
path: '/path/to/page',
pathNames: ['path', 'to', 'page'],
port: '8080',
protocol: 'https',
query: 'query=123',
queryKey: { query: '123' },
relative: '',
source: 'https://user:password@localhost:8080/path/to/page?query=123#section',
user: 'user',
userInfo: 'user:password',
},
},
{
input: 'ftp://ftp.example.com/resource',
expected: {
anchor: '',
authority: 'ftp.example.com',
directory: '',
file: 'resource',
host: 'ftp.example.com',
href: 'ftp://ftp.example.com/resource',
id: 'ftp://ftp.example.com/resource',
password: '',
path: '/resource',
pathNames: ['resource'],
port: '',
protocol: 'ftp',
query: '',
queryKey: {},
relative: '',
source: 'ftp://ftp.example.com/resource',
user: '',
userInfo: '',
},
},
...moreTestCases
];
testCases.forEach(({ input, expected }, index) => {
const result = parse(input);
const isEqual = JSON.stringify(result) === JSON.stringify(expected);
console.log(
`Test Case ${index + 1}: ${isEqual ? '✅ Passed' : '❌ Failed'}`,
);
if (!isEqual) {
console.log('Input:', input);
console.log('Expected:', expected);
console.log('Got:', result);
}
});
}
testParse();