This commit is contained in:
tamakyi
2026-09-08 10:10:44 +08:00
commit 2ed27dd55e
288 changed files with 67061 additions and 0 deletions
+127
View File
@@ -0,0 +1,127 @@
package shared
import (
"context"
"net"
"strconv"
"strings"
"testing"
"time"
)
// startLoopbackListener 在 127.0.0.1 上开一个监听并返回其端口(关闭由 t.Cleanup 托管)。
func startLoopbackListener(t *testing.T) int {
t.Helper()
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("listen: %v", err)
}
t.Cleanup(func() { _ = ln.Close() })
return ln.Addr().(*net.TCPAddr).Port
}
func testDialer() *net.Dialer {
return &net.Dialer{Timeout: 3 * time.Second, KeepAlive: 30 * time.Second}
}
// TestDialFirstReachablePicksReachableIP 验证多候选并发拨号会跳过不可达 IP、
// 选中可达的那个(顺序上不可达在前也不影响结果)。
func TestDialFirstReachablePicksReachableIP(t *testing.T) {
port := startLoopbackListener(t)
// 127.0.0.2 上没有任何监听 → 该候选必然拨号失败;
// 127.0.0.1:port 有监听 → 可达。把坏 IP 放前面,验证不受顺序影响。
conn, err := DialFirstReachable(context.Background(), testDialer(), "tcp",
[]net.IP{net.ParseIP("127.0.0.2"), net.ParseIP("127.0.0.1")}, strconv.Itoa(port), 6)
if err != nil {
t.Fatalf("DialFirstReachable returned error: %v", err)
}
if conn == nil {
t.Fatal("DialFirstReachable returned nil conn without error")
}
// 连接应落在可达的 127.0.0.1 上
remote := conn.RemoteAddr().(*net.TCPAddr)
if !remote.IP.Equal(net.ParseIP("127.0.0.1")) {
t.Fatalf("expected connection to 127.0.0.1, got %s", remote)
}
_ = conn.Close()
}
// TestDialFirstReachableAllFail 验证全部候选不可达时返回错误而非死等或空结果。
func TestDialFirstReachableAllFail(t *testing.T) {
// 先开再关,拿到一个确定无监听的端口
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("listen: %v", err)
}
closedPort := ln.Addr().(*net.TCPAddr).Port
_ = ln.Close()
start := time.Now()
conn, err := DialFirstReachable(context.Background(), testDialer(), "tcp",
[]net.IP{net.ParseIP("127.0.0.2"), net.ParseIP("127.0.0.1")}, strconv.Itoa(closedPort), 6)
if err == nil {
if conn != nil {
_ = conn.Close()
}
t.Fatal("expected error when all candidates unreachable, got nil")
}
if conn != nil {
_ = conn.Close()
t.Fatal("expected nil conn on total failure")
}
if elapsed := time.Since(start); elapsed > 15*time.Second {
t.Fatalf("all-fail path took too long: %v", elapsed)
}
}
// TestDialFirstReachableCtxCancel 验证父 context 取消后拨号立即返回(不泄漏、不死锁)。
func TestDialFirstReachableCtxCancel(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel() // 拨号前已取消
start := time.Now()
conn, err := DialFirstReachable(ctx, testDialer(), "tcp",
[]net.IP{net.ParseIP("127.0.0.1")}, "81", 6)
if err == nil {
if conn != nil {
_ = conn.Close()
}
t.Fatal("expected error after context cancel, got nil")
}
if conn != nil {
_ = conn.Close()
t.Fatal("expected nil conn on cancelled context")
}
if !strings.Contains(err.Error(), "canceled") && !strings.Contains(err.Error(), "deadline") {
t.Fatalf("expected context cancellation error, got: %v", err)
}
if elapsed := time.Since(start); elapsed > 5*time.Second {
t.Fatalf("context cancel path took too long: %v", elapsed)
}
}
// TestDialFirstReachableEmptyCandidates 验证空候选返回错误而非 panic。
func TestDialFirstReachableEmptyCandidates(t *testing.T) {
conn, err := DialFirstReachable(context.Background(), testDialer(), "tcp", nil, "80", 6)
if err == nil {
if conn != nil {
_ = conn.Close()
}
t.Fatal("expected error for empty candidates, got nil")
}
}
// TestDialFirstReachableMaxCandidatesCapped 验证 maxCandidates 截断候选数不会 panic。
func TestDialFirstReachableMaxCandidatesCapped(t *testing.T) {
port := startLoopbackListener(t)
conn, err := DialFirstReachable(context.Background(), testDialer(), "tcp",
[]net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("127.0.0.1")}, strconv.Itoa(port), 1)
if err != nil {
t.Fatalf("DialFirstReachable with capped candidates returned error: %v", err)
}
if conn == nil {
t.Fatal("DialFirstReachable returned nil conn without error")
}
_ = conn.Close()
}