import { describe, expect, it } from 'vitest' import { duplicateForwardRule, formatTransferBytes, parseForwardRules, portForwardStorageKey, 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')).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') }) })