Finder-launched terminals now inherit a login PATH so tools like ollama resolve, destructive SFTP/editor actions require confirmation, and terminal search plus window dragging work again. SSH forwards and RDP sessions keep listeners and survive reconnect without FastPath crashes. Co-authored-by: Cursor <cursoragent@cursor.com>
98 lines
3.2 KiB
TypeScript
98 lines
3.2 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import {
|
|
duplicateForwardRule,
|
|
formatHostPort,
|
|
formatTransferBytes,
|
|
parseForwardRules,
|
|
parseHostPort,
|
|
portForwardStorageKey,
|
|
resolveForwardTargetHost,
|
|
resolveRemoteBindHost,
|
|
runtimeMap,
|
|
} from '../../../src/components/connection/forwardRules'
|
|
|
|
describe('forward rule persistence', () => {
|
|
it('uses stable session ids instead of runtime connection ids', () => {
|
|
expect(portForwardStorageKey('saved-1', 'tab-1')).toBe('portForwards_session_saved-1')
|
|
expect(portForwardStorageKey(undefined, 'tab-1', 'conn-9')).toBe('portForwards_conn_conn-9')
|
|
expect(portForwardStorageKey(undefined, 'tab-1')).toBe('portForwards_tab_tab-1')
|
|
})
|
|
|
|
it('migrates JSON strings and drops runtime state', () => {
|
|
const rules = parseForwardRules(
|
|
JSON.stringify([
|
|
{
|
|
id: 'proxy-1',
|
|
type: 'dynamic',
|
|
name: 'SOCKS5',
|
|
localPort: 1080,
|
|
isActive: true,
|
|
},
|
|
])
|
|
)
|
|
expect(rules).toEqual([{ id: 'proxy-1', type: 'dynamic', name: 'SOCKS5', localPort: 1080 }])
|
|
expect(rules[0]).not.toHaveProperty('isActive')
|
|
})
|
|
|
|
it('indexes process-local tunnel status independently', () => {
|
|
const map = runtimeMap([
|
|
{
|
|
forwardId: 'proxy-1',
|
|
connectionId: 'conn-1',
|
|
kind: 'dynamic',
|
|
bindHost: '127.0.0.1',
|
|
boundPort: 1080,
|
|
startedAt: 1,
|
|
running: true,
|
|
activeConnections: 2,
|
|
bytesUp: 1024,
|
|
bytesDown: 2048,
|
|
},
|
|
])
|
|
expect(map.get('proxy-1')?.activeConnections).toBe(2)
|
|
expect(formatTransferBytes(1536)).toBe('1.5 KB')
|
|
})
|
|
|
|
it('duplicates configuration with a fresh id and no runtime state', () => {
|
|
const original = { id: 'proxy-1', type: 'dynamic' as const, name: 'SOCKS5', localPort: 1080 }
|
|
const first = duplicateForwardRule(original, [original], 'proxy-2')
|
|
const second = duplicateForwardRule(original, [original, first], 'proxy-3')
|
|
|
|
expect(first).toEqual({ ...original, id: 'proxy-2', name: 'SOCKS5 副本' })
|
|
expect(second.name).toBe('SOCKS5 副本 2')
|
|
expect(second).not.toHaveProperty('isActive')
|
|
})
|
|
|
|
it('parses and formats host:port listen/target addresses', () => {
|
|
expect(parseHostPort('127.0.0.1:18080')).toEqual({ host: '127.0.0.1', port: 18080 })
|
|
expect(parseHostPort('0.0.0.0:8080')).toEqual({ host: '0.0.0.0', port: 8080 })
|
|
expect(parseHostPort('bad')).toBeNull()
|
|
expect(formatHostPort('0.0.0.0', 8080)).toBe('0.0.0.0:8080')
|
|
expect(resolveForwardTargetHost('0.0.0.0')).toBe('127.0.0.1')
|
|
})
|
|
|
|
it('keeps remote listen bind including 0.0.0.0 for GatewayPorts', () => {
|
|
const rules = parseForwardRules([
|
|
{
|
|
id: 'r1',
|
|
type: 'remote',
|
|
name: 'expose',
|
|
localPort: 8080,
|
|
remoteBindHost: '0.0.0.0',
|
|
remoteHost: '192.168.1.10',
|
|
remotePort: 18080,
|
|
},
|
|
])
|
|
expect(rules[0]).toMatchObject({
|
|
type: 'remote',
|
|
remoteBindHost: '0.0.0.0',
|
|
remoteHost: '192.168.1.10',
|
|
remotePort: 18080,
|
|
localPort: 8080,
|
|
})
|
|
expect(resolveRemoteBindHost('localhost')).toBe('127.0.0.1')
|
|
expect(resolveRemoteBindHost('0.0.0.0')).toBe('0.0.0.0')
|
|
})
|
|
})
|