Files

140 lines
3.8 KiB
Go

package proxy
import (
"fmt"
"net"
"os"
"path/filepath"
"testing"
)
func TestXrayRegisterBridgeStoresNewBridge(t *testing.T) {
t.Parallel()
manager := &XrayManager{
Bridges: make(map[string]*XrayBridge),
}
bridge := &XrayBridge{
NodeKey: "node-a",
Port: 21001,
Running: true,
}
socksURL, reused := manager.registerBridge("node-a", bridge, false)
if reused {
t.Fatalf("expected new bridge registration, got reused with %q", socksURL)
}
if socksURL != "" {
t.Fatalf("expected empty socksURL for new bridge registration, got %q", socksURL)
}
if manager.Bridges["node-a"] != bridge {
t.Fatalf("bridge was not stored in manager")
}
}
func TestXrayRegisterBridgeIgnoresSamePointer(t *testing.T) {
t.Parallel()
manager := &XrayManager{
Bridges: make(map[string]*XrayBridge),
}
bridge := &XrayBridge{
NodeKey: "node-a",
Port: 21001,
Running: true,
}
manager.Bridges["node-a"] = bridge
socksURL, reused := manager.registerBridge("node-a", bridge, false)
if reused {
t.Fatalf("same bridge pointer must not be treated as duplicate, got reused with %q", socksURL)
}
if socksURL != "" {
t.Fatalf("expected empty socksURL when registering same pointer, got %q", socksURL)
}
if manager.Bridges["node-a"] != bridge {
t.Fatalf("bridge mapping changed unexpectedly")
}
if bridge.Stopping {
t.Fatalf("same bridge pointer should not be marked as stopping")
}
}
func TestXrayLaunchErrorRetryClassification(t *testing.T) {
t.Parallel()
if isRetryableXrayLaunchError(&xrayLaunchError{err: fmt.Errorf("config invalid"), retryable: false}) {
t.Fatalf("non-retryable xray launch error was classified as retryable")
}
if !isRetryableXrayLaunchError(&xrayLaunchError{err: fmt.Errorf("port race"), retryable: true}) {
t.Fatalf("retryable xray launch error was classified as non-retryable")
}
if !isRetryableXrayLaunchError(fmt.Errorf("legacy error")) {
t.Fatalf("plain errors should remain retryable for backward compatibility")
}
}
func TestXrayBridgeReadyErrorRetryPolicy(t *testing.T) {
t.Parallel()
manager := &XrayManager{}
dir := t.TempDir()
cfgPath := filepath.Join(dir, "xray-config.json")
stderrPath := filepath.Join(dir, "xray-stderr.log")
if err := os.WriteFile(cfgPath, []byte(`{}`), 0o644); err != nil {
t.Fatalf("write config failed: %v", err)
}
if manager.isRetryableBridgeReadyError(fmt.Errorf("xray 进程提前退出: config invalid"), cfgPath, stderrPath) {
t.Fatalf("early process exit without bind evidence should not be retried")
}
if err := os.WriteFile(stderrPath, []byte("listen tcp 127.0.0.1:10001: bind: address already in use"), 0o644); err != nil {
t.Fatalf("write stderr failed: %v", err)
}
if !manager.isRetryableBridgeReadyError(fmt.Errorf("xray 进程提前退出"), cfgPath, stderrPath) {
t.Fatalf("bind conflict should be retried with another port")
}
}
func TestXrayRegisterBridgeTransfersRestartingRefCount(t *testing.T) {
t.Parallel()
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("listen failed: %v", err)
}
defer listener.Close()
port := listener.Addr().(*net.TCPAddr).Port
go func() {
for {
conn, err := listener.Accept()
if err != nil {
return
}
_ = conn.Close()
}
}()
manager := &XrayManager{Bridges: make(map[string]*XrayBridge)}
oldBridge := &XrayBridge{
NodeKey: "node-a",
Port: 21001,
Running: false,
RefCount: 2,
Restarting: true,
}
manager.Bridges["node-a"] = oldBridge
newBridge := &XrayBridge{NodeKey: "node-a", Port: port, Running: true}
socksURL, reused := manager.registerBridge("node-a", newBridge, false)
if reused {
t.Fatalf("expected new restarted bridge registration, got reused %q", socksURL)
}
if manager.Bridges["node-a"] != newBridge {
t.Fatalf("new bridge was not registered")
}
if newBridge.RefCount != 2 {
t.Fatalf("restart refcount = %d, want 2", newBridge.RefCount)
}
}