feat(proxy): HTTP/SOCKS5 代理支持,按会话独立配置 | 15 files +483/-23 | 96/96 tests passing
功能: - 代理类型定义 (ProxyType, ProxyConfig) - 代理工具函数 (验证、解析、构建URL) + 24 测试 - SSH 连接通过 HTTP CONNECT / SOCKS5 隧道 - 数据库 proxy 列持久化 (JSON) - SessionDialog 代理配置 UI (可折叠面板) - 连接池传递 proxy 到 IPC - sessionStore 类型添加 proxy 支持
This commit is contained in:
1 parent
3f636b1b0e
commit
07c4a5b14f
15 files changed
+757
-23
No files matched your search
@@ -0,0 +1,203 @@
|
||||
/**
|
||||
* Proxy Config Utils - Tests
|
||||
* 代理配置验证和工具函数测试
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import {
|
||||
validateProxyConfig,
|
||||
parseProxyUrl,
|
||||
buildProxyUrl,
|
||||
isProxyConfigured,
|
||||
getDefaultProxyConfig,
|
||||
} from '../../src/utils/proxy'
|
||||
import type { ProxyConfig } from '../../shared/types/session.types'
|
||||
|
||||
describe('Proxy Utils', () => {
|
||||
describe('getDefaultProxyConfig', () => {
|
||||
it('应返回默认代理配置', () => {
|
||||
const config = getDefaultProxyConfig()
|
||||
expect(config).toEqual({
|
||||
type: 'none',
|
||||
host: '',
|
||||
port: 0,
|
||||
username: '',
|
||||
password: '',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('isProxyConfigured', () => {
|
||||
it('type=none 应返回 false', () => {
|
||||
expect(isProxyConfigured({ type: 'none', host: '', port: 0 })).toBe(false)
|
||||
})
|
||||
|
||||
it('type=http 有 host 应返回 true', () => {
|
||||
expect(isProxyConfigured({ type: 'http', host: 'proxy.example.com', port: 8080 })).toBe(true)
|
||||
})
|
||||
|
||||
it('type=socks5 有 host 应返回 true', () => {
|
||||
expect(isProxyConfigured({ type: 'socks5', host: '127.0.0.1', port: 1080 })).toBe(true)
|
||||
})
|
||||
|
||||
it('type=http 但 host 为空应返回 false', () => {
|
||||
expect(isProxyConfigured({ type: 'http', host: '', port: 8080 })).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('validateProxyConfig', () => {
|
||||
it('type=none 应始终有效', () => {
|
||||
const result = validateProxyConfig({ type: 'none', host: '', port: 0 })
|
||||
expect(result.valid).toBe(true)
|
||||
expect(result.error).toBeUndefined()
|
||||
})
|
||||
|
||||
it('有效的 HTTP 代理配置应通过验证', () => {
|
||||
const config: ProxyConfig = { type: 'http', host: 'proxy.example.com', port: 8080 }
|
||||
const result = validateProxyConfig(config)
|
||||
expect(result.valid).toBe(true)
|
||||
})
|
||||
|
||||
it('有效的 SOCKS5 代理配置应通过验证', () => {
|
||||
const config: ProxyConfig = { type: 'socks5', host: '127.0.0.1', port: 1080 }
|
||||
const result = validateProxyConfig(config)
|
||||
expect(result.valid).toBe(true)
|
||||
})
|
||||
|
||||
it('host 为空应验证失败', () => {
|
||||
const config: ProxyConfig = { type: 'http', host: '', port: 8080 }
|
||||
const result = validateProxyConfig(config)
|
||||
expect(result.valid).toBe(false)
|
||||
expect(result.error).toContain('host')
|
||||
})
|
||||
|
||||
it('port 为 0 应验证失败', () => {
|
||||
const config: ProxyConfig = { type: 'http', host: 'proxy.example.com', port: 0 }
|
||||
const result = validateProxyConfig(config)
|
||||
expect(result.valid).toBe(false)
|
||||
expect(result.error).toContain('port')
|
||||
})
|
||||
|
||||
it('port 为负数应验证失败', () => {
|
||||
const config: ProxyConfig = { type: 'socks5', host: '127.0.0.1', port: -1 }
|
||||
const result = validateProxyConfig(config)
|
||||
expect(result.valid).toBe(false)
|
||||
expect(result.error).toContain('port')
|
||||
})
|
||||
|
||||
it('port 超过 65535 应验证失败', () => {
|
||||
const config: ProxyConfig = { type: 'http', host: 'proxy.example.com', port: 70000 }
|
||||
const result = validateProxyConfig(config)
|
||||
expect(result.valid).toBe(false)
|
||||
expect(result.error).toContain('port')
|
||||
})
|
||||
|
||||
it('host 含有非法字符应验证失败', () => {
|
||||
const config: ProxyConfig = { type: 'http', host: 'proxy example.com', port: 8080 }
|
||||
const result = validateProxyConfig(config)
|
||||
expect(result.valid).toBe(false)
|
||||
expect(result.error).toContain('host')
|
||||
})
|
||||
|
||||
it('带用户名密码的有效代理应通过验证', () => {
|
||||
const config: ProxyConfig = {
|
||||
type: 'http',
|
||||
host: 'proxy.example.com',
|
||||
port: 8080,
|
||||
username: 'user',
|
||||
password: 'pass',
|
||||
}
|
||||
const result = validateProxyConfig(config)
|
||||
expect(result.valid).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('parseProxyUrl', () => {
|
||||
it('应解析 HTTP 代理 URL', () => {
|
||||
const result = parseProxyUrl('http://proxy.example.com:8080')
|
||||
expect(result).toEqual({
|
||||
type: 'http',
|
||||
host: 'proxy.example.com',
|
||||
port: 8080,
|
||||
username: '',
|
||||
password: '',
|
||||
})
|
||||
})
|
||||
|
||||
it('应解析 SOCKS5 代理 URL', () => {
|
||||
const result = parseProxyUrl('socks5://127.0.0.1:1080')
|
||||
expect(result).toEqual({
|
||||
type: 'socks5',
|
||||
host: '127.0.0.1',
|
||||
port: 1080,
|
||||
username: '',
|
||||
password: '',
|
||||
})
|
||||
})
|
||||
|
||||
it('应解析带认证的代理 URL', () => {
|
||||
const result = parseProxyUrl('http://user:pass@proxy.example.com:3128')
|
||||
expect(result).toEqual({
|
||||
type: 'http',
|
||||
host: 'proxy.example.com',
|
||||
port: 3128,
|
||||
username: 'user',
|
||||
password: 'pass',
|
||||
})
|
||||
})
|
||||
|
||||
it('应解析无端口的 URL (使用默认端口)', () => {
|
||||
const result = parseProxyUrl('http://proxy.example.com')
|
||||
expect(result).toEqual({
|
||||
type: 'http',
|
||||
host: 'proxy.example.com',
|
||||
port: 80,
|
||||
username: '',
|
||||
password: '',
|
||||
})
|
||||
})
|
||||
|
||||
it('SOCKS5 无端口应使用 1080', () => {
|
||||
const result = parseProxyUrl('socks5://proxy.example.com')
|
||||
expect(result).toEqual({
|
||||
type: 'socks5',
|
||||
host: 'proxy.example.com',
|
||||
port: 1080,
|
||||
username: '',
|
||||
password: '',
|
||||
})
|
||||
})
|
||||
|
||||
it('无效 URL 应返回 null', () => {
|
||||
expect(parseProxyUrl('')).toBeNull()
|
||||
expect(parseProxyUrl('not-a-url')).toBeNull()
|
||||
})
|
||||
|
||||
it('不支持的协议应返回 null', () => {
|
||||
expect(parseProxyUrl('ftp://proxy.example.com:21')).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('buildProxyUrl', () => {
|
||||
it('应构建 HTTP 代理 URL', () => {
|
||||
const config: ProxyConfig = { type: 'http', host: 'proxy.example.com', port: 8080 }
|
||||
expect(buildProxyUrl(config)).toBe('http://proxy.example.com:8080')
|
||||
})
|
||||
|
||||
it('应构建带认证的代理 URL', () => {
|
||||
const config: ProxyConfig = {
|
||||
type: 'socks5',
|
||||
host: '127.0.0.1',
|
||||
port: 1080,
|
||||
username: 'user',
|
||||
password: 'pass',
|
||||
}
|
||||
expect(buildProxyUrl(config)).toBe('socks5://user:pass@127.0.0.1:1080')
|
||||
})
|
||||
|
||||
it('type=none 应返回空字符串', () => {
|
||||
const config: ProxyConfig = { type: 'none', host: '', port: 0 }
|
||||
expect(buildProxyUrl(config)).toBe('')
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user