Files
team-api/relaykit/dispatch/score_test.go
T

155 lines
4.2 KiB
Go

package dispatch
import (
"testing"
"github.com/stretchr/testify/assert"
)
func healthyChannel(id int64, tier Tier, weight float64) Channel {
return Channel{
ID: id,
Tier: tier,
BaseWeight: weight,
SuccEwma: 1.0,
RampElapsedMs: -1,
}
}
func TestEffectiveWeight_FullHealthBaseline(t *testing.T) {
pol := DefaultRoutingPolicy()
w, bd := EffectiveWeight(healthyChannel(1, TierPrimary, 10), pol)
assert.InDelta(t, 10.0, w, 1e-9)
assert.Equal(t, 1.0, bd.Tier)
assert.Equal(t, 1.0, bd.Health)
assert.Equal(t, 1.0, bd.Headroom)
assert.Equal(t, 1.0, bd.Cost)
assert.Equal(t, 1.0, bd.Ramp)
}
func TestEffectiveWeight_TierBias(t *testing.T) {
pol := DefaultRoutingPolicy()
wp, _ := EffectiveWeight(healthyChannel(1, TierPrimary, 10), pol)
ws, _ := EffectiveWeight(healthyChannel(2, TierSecondary, 10), pol)
wr, _ := EffectiveWeight(healthyChannel(3, TierReserve, 10), pol)
assert.InDelta(t, wp*0.15, ws, 1e-9)
assert.InDelta(t, wp*0.02, wr, 1e-9)
// 未知层级(策略未配置)权重为 0
wu, _ := EffectiveWeight(healthyChannel(4, Tier("unknown"), 10), pol)
assert.Zero(t, wu)
}
func TestEffectiveWeight_HealthFactor(t *testing.T) {
pol := DefaultRoutingPolicy() // alpha=2
c := healthyChannel(1, TierPrimary, 10)
c.SuccEwma = 0.9
_, bd := EffectiveWeight(c, pol)
assert.InDelta(t, 0.81, bd.Health, 1e-9, "succ=0.9 → 0.81")
c.SuccEwma = 0.5
_, bd = EffectiveWeight(c, pol)
assert.InDelta(t, 0.25, bd.Health, 1e-9)
// 下限保护:极低成功率不至于权重归零(区别于熔断硬排除)
c.SuccEwma = 0.001
w, _ := EffectiveWeight(c, pol)
assert.Greater(t, w, 0.0)
// 延迟惩罚:超过 latRef 按比例衰减,最多减半
c = healthyChannel(1, TierPrimary, 10)
c.LatEwmaMs = 6000 // latRef=3000 → penalty 0.5
_, bd = EffectiveWeight(c, pol)
assert.InDelta(t, 0.5, bd.Health, 1e-9)
c.LatEwmaMs = 100000 // penalty 钳制在 0.5
_, bd = EffectiveWeight(c, pol)
assert.InDelta(t, 0.5, bd.Health, 1e-9)
// 无健康数据(新渠道)视为满分
c = Channel{ID: 9, Tier: TierPrimary, BaseWeight: 10, RampElapsedMs: -1}
_, bd = EffectiveWeight(c, pol)
assert.Equal(t, 1.0, bd.Health)
}
func TestEffectiveWeight_HeadroomFactor(t *testing.T) {
pol := DefaultRoutingPolicy() // gamma=2
c := healthyChannel(1, TierPrimary, 10)
c.SoftLimit = 10
c.Inflight = 5
_, bd := EffectiveWeight(c, pol)
assert.InDelta(t, 0.25, bd.Headroom, 1e-9, "余量 0.5 → 0.5^2")
// 饱和 → 权重坍缩为 0
c.Inflight = 10
w, _ := EffectiveWeight(c, pol)
assert.Zero(t, w)
// 超卖同样为 0
c.Inflight = 15
w, _ = EffectiveWeight(c, pol)
assert.Zero(t, w)
// 无容量信息恒为 1
c.SoftLimit = 0
c.Inflight = 999
_, bd = EffectiveWeight(c, pol)
assert.Equal(t, 1.0, bd.Headroom)
}
func TestEffectiveWeight_CostFactor(t *testing.T) {
pol := DefaultRoutingPolicy() // beta=0.5, clamp [0.5, 2.0]
c := healthyChannel(1, TierPrimary, 10)
c.CostRatio = 0.8 // 八折渠道 → (1/0.8)^0.5 ≈ 1.118
_, bd := EffectiveWeight(c, pol)
assert.InDelta(t, 1.118, bd.Cost, 0.001)
c.CostRatio = 100 // 极贵渠道钳制在下限
_, bd = EffectiveWeight(c, pol)
assert.Equal(t, 0.5, bd.Cost)
c.CostRatio = 0.0001 // 极便宜钳制在上限
_, bd = EffectiveWeight(c, pol)
assert.Equal(t, 2.0, bd.Cost)
c.CostRatio = 0 // 未配置视为等价
_, bd = EffectiveWeight(c, pol)
assert.Equal(t, 1.0, bd.Cost)
}
func TestEffectiveWeight_RampFactor(t *testing.T) {
pol := DefaultRoutingPolicy() // window=120s, floor=0.05
c := healthyChannel(1, TierPrimary, 10)
c.RampElapsedMs = 0
_, bd := EffectiveWeight(c, pol)
assert.Equal(t, 0.05, bd.Ramp, "刚恢复从 floor 开始")
c.RampElapsedMs = 60_000
_, bd = EffectiveWeight(c, pol)
assert.InDelta(t, 0.5, bd.Ramp, 1e-9)
c.RampElapsedMs = 120_000
_, bd = EffectiveWeight(c, pol)
assert.Equal(t, 1.0, bd.Ramp)
c.RampElapsedMs = -1
_, bd = EffectiveWeight(c, pol)
assert.Equal(t, 1.0, bd.Ramp, "非爬坡期恒为 1")
}
func TestEffectiveWeight_NonPositiveBaseWeight(t *testing.T) {
pol := DefaultRoutingPolicy()
c := healthyChannel(1, TierPrimary, 0)
w, _ := EffectiveWeight(c, pol)
assert.Zero(t, w)
c.BaseWeight = -5
w, _ = EffectiveWeight(c, pol)
assert.Zero(t, w)
}