- proxypool: 站点级出口绑定 proxies = [条目名, "sub:订阅名"],粘性选路、 失败切换、后台健康探测;支持 Xray 分享链接 / 订阅 / 裸 ip:port 与 socks5 端点, 托管 Xray 核心(可自动下载);站点未绑定时回退 [proxy] 主/备线路 - upstream: RouteSource 抽象 + routesFor/poolReport;求解与请求共用同一出口 - admin: 代理池面板与维护动作;overview 暴露池状态
77 lines
1.9 KiB
Go
77 lines
1.9 KiB
Go
package proxypool
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/nyoungo/dsfree2api/internal/config"
|
|
)
|
|
|
|
const examplePath = "../../config.example.toml"
|
|
|
|
func TestCandidatesStickySwitchOnFailure(t *testing.T) {
|
|
cfg, err := config.Load(examplePath)
|
|
if err != nil {
|
|
t.Fatalf("load config: %v", err)
|
|
}
|
|
cfg.ProxyPool.Enabled = true
|
|
cfg.ProxyPool.Entries = map[string]*config.ProxyEntry{
|
|
"a": {Link: "http://127.0.0.1:9", Enabled: true},
|
|
"b": {Link: "socks5://127.0.0.1:10", Enabled: true},
|
|
}
|
|
cfg.Sites["de"].Proxies = []string{"a", "b"}
|
|
|
|
m := New(cfg, t.TempDir(), nil)
|
|
m.Reload()
|
|
|
|
cands := m.Candidates("de")
|
|
if len(cands) != 2 || cands[0].Name != "a" || cands[0].Proxy != "http://127.0.0.1:9" {
|
|
t.Fatalf("candidates = %+v", cands)
|
|
}
|
|
|
|
// Both healthy: the sticky pick stays a.
|
|
m.mu.Lock()
|
|
for _, ep := range m.endpoints {
|
|
ep.checked = true
|
|
ep.healthy = true
|
|
}
|
|
m.mu.Unlock()
|
|
cands = m.Candidates("de")
|
|
if cands[0].Name != "a" {
|
|
t.Fatalf("sticky pick changed without failure: %+v", cands)
|
|
}
|
|
|
|
// a fails: the request path must immediately switch to b.
|
|
m.Report("http://127.0.0.1:9", false)
|
|
cands = m.Candidates("de")
|
|
if len(cands) != 1 || cands[0].Name != "b" {
|
|
t.Fatalf("after failure = %+v", cands)
|
|
}
|
|
|
|
// Everything unhealthy: fall back to the bound order so requests still try.
|
|
m.Report("socks5://127.0.0.1:10", false)
|
|
cands = m.Candidates("de")
|
|
if len(cands) != 2 || cands[0].Name != "a" {
|
|
t.Fatalf("all-unhealthy fallback = %+v", cands)
|
|
}
|
|
}
|
|
|
|
func TestCandidatesDisabledOrUnbound(t *testing.T) {
|
|
cfg, err := config.Load(examplePath)
|
|
if err != nil {
|
|
t.Fatalf("load config: %v", err)
|
|
}
|
|
cfg.ProxyPool.Enabled = false
|
|
cfg.Sites["de"].Proxies = []string{"a"}
|
|
m := New(cfg, t.TempDir(), nil)
|
|
m.Reload()
|
|
if got := m.Candidates("de"); got != nil {
|
|
t.Fatalf("disabled pool returned %+v", got)
|
|
}
|
|
|
|
cfg.ProxyPool.Enabled = true
|
|
m.Reload()
|
|
if got := m.Candidates("de"); got != nil {
|
|
t.Fatalf("unbound/unknown entry returned %+v", got)
|
|
}
|
|
}
|