Sub2API v1.0 - AI API 网关(二开初始版本,基于上游 Wei-Shaw/sub2api)
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s

This commit is contained in:
李建琦
2026-08-21 18:30:13 +08:00
commit 6d655c9903
3584 changed files with 1270640 additions and 0 deletions
@@ -0,0 +1,75 @@
package repository
import (
"context"
"net"
"net/url"
"testing"
"time"
"github.com/stretchr/testify/require"
)
// 回归:上游 Transport 必须显式配置建连超时。
//
// http.Transport.DialContext 为 nil 时 Go 使用零值 net.DialerTimeout=0),
// DNS 解析与 TCP 握手没有任何上限,只能等内核重传耗尽(Linux 约 130 秒)。
// ResponseHeaderTimeout 只覆盖连接建立之后的阶段,管不到建连。
// 上游域名被解析到不可达 IP 时,串行的多账号故障转移会把一次请求拖到数分钟。
func TestBuildUpstreamTransportSetsDialTimeout(t *testing.T) {
settings := defaultPoolSettings(nil)
transport, err := buildUpstreamTransport(settings, nil, upstreamProtocolModeDefault)
require.NoError(t, err)
require.NotNil(t, transport.DialContext, "DialContext 缺失会退化为无超时的零值 dialer")
require.Equal(t, defaultUpstreamTLSHandshakeTimeout, transport.TLSHandshakeTimeout)
}
func TestNewUpstreamDialerHasBoundedTimeout(t *testing.T) {
dialer := newUpstreamDialer()
require.Greater(t, dialer.Timeout, time.Duration(0), "建连超时必须有上限")
require.Equal(t, defaultUpstreamDialTimeout, dialer.Timeout)
require.Equal(t, defaultUpstreamDialKeepAlive, dialer.KeepAlive)
}
// 建连超时对 HTTP 代理同样生效:Transport.Proxy 走的仍是 DialContext
// 代理地址不可达时必须快速失败而不是挂满内核超时。
func TestBuildUpstreamTransportKeepsDialTimeoutWithHTTPProxy(t *testing.T) {
proxyURL, err := url.Parse("http://127.0.0.1:1080")
require.NoError(t, err)
transport, err := buildUpstreamTransport(defaultPoolSettings(nil), proxyURL, upstreamProtocolModeDefault)
require.NoError(t, err)
require.NotNil(t, transport.Proxy)
require.NotNil(t, transport.DialContext)
}
// SOCKS5 分支会覆盖 Transport.DialContext,覆盖后仍必须是有超时的拨号器。
func TestBuildUpstreamTransportKeepsDialContextWithSOCKS5Proxy(t *testing.T) {
proxyURL, err := url.Parse("socks5h://127.0.0.1:1080")
require.NoError(t, err)
transport, err := buildUpstreamTransport(defaultPoolSettings(nil), proxyURL, upstreamProtocolModeDefault)
require.NoError(t, err)
require.NotNil(t, transport.DialContext)
}
// Timeout 字段确实被 net.Dialer 用于建连:拨一个已被 close 的本地监听端口,
// 断言 Dialer 走的是自己的超时路径而不是无限等待。
// (不依赖外网可达性,CI 中确定性执行。)
func TestUpstreamDialerRespectsContextCancellation(t *testing.T) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
addr := listener.Addr().String()
require.NoError(t, listener.Close())
ctx, cancel := context.WithCancel(context.Background())
cancel()
conn, err := newUpstreamDialer().DialContext(ctx, "tcp", addr)
if conn != nil {
_ = conn.Close()
}
require.Error(t, err, "已取消的 context 必须立即中止拨号")
}