package accounts_test import ( "errors" "testing" "github.com/wangjia/pay/config" "github.com/wangjia/pay/internal/accounts" ) func newReg() *accounts.Registry { return accounts.New([]config.AccountConfig{ {AccountID: "f1", Channel: "fake", Region: "global", Enabled: true, Weight: 1}, {AccountID: "f2", Channel: "fake", Region: "global", Enabled: true, Weight: 1}, {AccountID: "f3", Channel: "fake", Region: "global", Enabled: false, Weight: 1}, // 禁用不参与 {AccountID: "a1", Channel: "alipay", Region: "cn", Enabled: true, Weight: 1}, }) } func TestRouterDefaultRoundRobin(t *testing.T) { r := accounts.NewRouter(newReg(), nil, nil) // 无 routing → 默认 round_robin seq := []string{"f1", "f2", "f1"} for i, w := range seq { got, err := r.Pick("fake", "global", accounts.PickHint{OutTradeNo: "PAY-1"}) if err != nil || got.AccountID != w { t.Fatalf("call %d got %v err %v want %s", i, got.AccountID, err, w) } } } func TestRouterExcludeSwitchesAccount(t *testing.T) { r := accounts.NewRouter(newReg(), map[string]string{"fake": accounts.StrategyWeighted}, nil) base, _ := r.Pick("fake", "global", accounts.PickHint{OutTradeNo: "PAY-RETRY"}) // retry:排除首选账户 → 必换到另一个。 got, err := r.Pick("fake", "global", accounts.PickHint{OutTradeNo: "PAY-RETRY", ExcludeAccounts: []string{base.AccountID}}) if err != nil || got.AccountID == base.AccountID { t.Fatalf("排除 %s 后应换账户, got %v err %v", base.AccountID, got.AccountID, err) } } func TestRouterExcludeEmptyFallsBackToAll(t *testing.T) { r := accounts.NewRouter(newReg(), nil, nil) // alipay/cn 只有 a1;排除 a1 后候选空 → 回退全集,仍返回 a1(单账户 retry 不失败)。 got, err := r.Pick("alipay", "cn", accounts.PickHint{OutTradeNo: "PAY-2", ExcludeAccounts: []string{"a1"}}) if err != nil || got.AccountID != "a1" { t.Fatalf("排除到空应回退全集, got %v err %v", got.AccountID, err) } } func TestRouterNoAccount(t *testing.T) { r := accounts.NewRouter(newReg(), nil, nil) if _, err := r.Pick("wechat", "cn", accounts.PickHint{}); !errors.Is(err, accounts.ErrNoAccount) { t.Fatalf("无账户渠道应 ErrNoAccount, got %v", err) } }