Files
yml2213 bc366fd6b9 feat(proxy): 支持多实例独立代理与短效API动态提取
- 实例配置独立:支持为每个实例单独配置 http/https/socks5 代理,避免单机多开挤兑触发斗鱼出口 IP 限流
- 动态 API 提取:原生支持星空代理等平台的 API 提取(兼容 JSON/文本格式),提供前端一键提取填入与连通性/延迟测速
- 定时前自动拉取:针对 3 分钟短效代理,支持在开抢前 15 秒自动提取并绑定最新 IP,并在预热前完成热连接与时间复校
- 修复复校竞态:将 resyncBeforeTrigger 从 3s 提升至 8s,确保三轮采样在开抢前顺利落盘,消除时钟漂移
- 验证方式:通过 go test ./... 全量测试与前端 vue-tsc/vite 编译,实测通过星空代理 API 解析测试用例
2026-10-24 19:26:16 +08:00

102 lines
2.9 KiB
Go

package client
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
)
func TestParseProxyURL(t *testing.T) {
tests := []struct {
input string
expectURL string
expectError bool
}{
{"", "", false},
{" ", "", false},
{"127.0.0.1:8888", "http://127.0.0.1:8888", false},
{"http://127.0.0.1:8888", "http://127.0.0.1:8888", false},
{"https://127.0.0.1:8888", "https://127.0.0.1:8888", false},
{"socks5://127.0.0.1:1080", "socks5://127.0.0.1:1080", false},
{"http://user:pass@127.0.0.1:8888", "http://user:pass@127.0.0.1:8888", false},
{"ftp://127.0.0.1:21", "", true},
}
for _, tc := range tests {
u, err := ParseProxyURL(tc.input)
if tc.expectError {
if err == nil {
t.Fatalf("ParseProxyURL(%q) expected error, got nil", tc.input)
}
} else {
if err != nil {
t.Fatalf("ParseProxyURL(%q) unexpected error: %v", tc.input, err)
}
if tc.expectURL == "" {
if u != nil {
t.Fatalf("ParseProxyURL(%q) expected nil, got %v", tc.input, u)
}
} else if u.String() != tc.expectURL {
t.Fatalf("ParseProxyURL(%q) = %q, want %q", tc.input, u.String(), tc.expectURL)
}
}
}
}
func TestFetchProxyFromAPI_XKDaili(t *testing.T) {
// 模拟星空代理正常返回
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
fmt.Fprint(w, `{ "status":100 ,"info":"成功" , "data": [{"ip": "115.230.134.174", "port": "40014", "prov": "浙江", "city": "嘉兴"}]}`)
}))
defer ts.Close()
res, err := FetchProxyFromAPI(ts.URL)
if err != nil {
t.Fatalf("FetchProxyFromAPI failed: %v", err)
}
if res.IP != "115.230.134.174" || res.Port != "40014" {
t.Fatalf("unexpected res: %+v", res)
}
if res.ProxyURL != "http://115.230.134.174:40014" {
t.Fatalf("unexpected proxy url: %s", res.ProxyURL)
}
if res.Region != "浙江 嘉兴" {
t.Fatalf("unexpected region: %s", res.Region)
}
}
func TestFetchProxyFromAPI_Error(t *testing.T) {
// 模拟星空代理未加白名单报错
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
fmt.Fprint(w, `{ "status":211 , "info":"当前IP不在白名单内" }`)
}))
defer ts.Close()
_, err := FetchProxyFromAPI(ts.URL)
if err == nil {
t.Fatal("expected error, got nil")
}
if err.Error() != "代理平台返回: 当前IP不在白名单内 (状态码=211)" {
t.Fatalf("unexpected error string: %v", err)
}
}
func TestFetchProxyFromAPI_Text(t *testing.T) {
// 模拟纯文本返回
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "221.122.91.45:3128\r\n")
}))
defer ts.Close()
res, err := FetchProxyFromAPI(ts.URL)
if err != nil {
t.Fatalf("FetchProxyFromAPI failed: %v", err)
}
if res.ProxyURL != "http://221.122.91.45:3128" {
t.Fatalf("unexpected proxy url: %s", res.ProxyURL)
}
}