mirror of
https://github.com/openziti/ziti.git
synced 2026-09-11 13:29:03 +00:00
132 lines
3.7 KiB
Go
132 lines
3.7 KiB
Go
package xt_weighted
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/openziti/ziti/v2/controller/xt"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type mockTerminator struct {
|
|
id string
|
|
precedence xt.Precedence
|
|
routeCost uint32
|
|
}
|
|
|
|
func (m *mockTerminator) GetId() string { return m.id }
|
|
func (m *mockTerminator) GetPrecedence() xt.Precedence { return m.precedence }
|
|
func (m *mockTerminator) GetRouteCost() uint32 { return m.routeCost }
|
|
func (m *mockTerminator) GetCost() uint16 { return 0 }
|
|
func (m *mockTerminator) GetServiceId() string { return "svc" }
|
|
func (m *mockTerminator) GetInstanceId() string { return "" }
|
|
func (m *mockTerminator) GetRouterId() string { return "r1" }
|
|
func (m *mockTerminator) GetBinding() string { return "" }
|
|
func (m *mockTerminator) GetAddress() string { return "" }
|
|
func (m *mockTerminator) GetPeerData() xt.PeerData { return nil }
|
|
func (m *mockTerminator) GetCreatedAt() time.Time { return time.Time{} }
|
|
func (m *mockTerminator) GetHostId() string { return "" }
|
|
func (m *mockTerminator) GetSourceCtrl() string { return "" }
|
|
|
|
func newTerminator(id string, cost uint32) xt.CostedTerminator {
|
|
return &mockTerminator{
|
|
id: id,
|
|
precedence: xt.Precedences.Default,
|
|
routeCost: xt.Precedences.Default.GetBiasedCost(cost),
|
|
}
|
|
}
|
|
|
|
func TestWeightedStrategy_AllTerminatorsSelected(t *testing.T) {
|
|
req := require.New(t)
|
|
|
|
s := &strategy{}
|
|
|
|
terminators := []xt.CostedTerminator{
|
|
newTerminator("a", 10),
|
|
newTerminator("b", 10),
|
|
newTerminator("c", 10),
|
|
}
|
|
|
|
counts := map[string]int{}
|
|
iterations := 30000
|
|
|
|
for range iterations {
|
|
selected, _, err := s.Select(nil, terminators)
|
|
req.NoError(err)
|
|
counts[selected.GetId()]++
|
|
}
|
|
|
|
// With equal costs, each should be selected roughly 1/3 of the time.
|
|
// Use a generous tolerance of 15% to avoid flaky tests.
|
|
expectedPer := float64(iterations) / 3.0
|
|
tolerance := 0.15
|
|
|
|
for _, id := range []string{"a", "b", "c"} {
|
|
count := counts[id]
|
|
req.Greater(count, 0, "terminator %s was never selected", id)
|
|
deviation := math.Abs(float64(count)-expectedPer) / expectedPer
|
|
req.Less(deviation, tolerance,
|
|
"terminator %s selected %d times (expected ~%d, deviation %.1f%%)",
|
|
id, count, int(expectedPer), deviation*100)
|
|
}
|
|
}
|
|
|
|
func TestWeightedStrategy_LowerCostPreferred(t *testing.T) {
|
|
req := require.New(t)
|
|
|
|
s := &strategy{}
|
|
|
|
terminators := []xt.CostedTerminator{
|
|
newTerminator("cheap", 1),
|
|
newTerminator("expensive", 100),
|
|
}
|
|
|
|
counts := map[string]int{}
|
|
iterations := 10000
|
|
|
|
for range iterations {
|
|
selected, _, err := s.Select(nil, terminators)
|
|
req.NoError(err)
|
|
counts[selected.GetId()]++
|
|
}
|
|
|
|
// The cheaper terminator should be selected significantly more often
|
|
req.Greater(counts["cheap"], counts["expensive"],
|
|
"cheaper terminator should be selected more often: cheap=%d, expensive=%d",
|
|
counts["cheap"], counts["expensive"])
|
|
}
|
|
|
|
func TestWeightedStrategy_FiveTerminatorsAllSelected(t *testing.T) {
|
|
req := require.New(t)
|
|
|
|
s := &strategy{}
|
|
|
|
terminators := []xt.CostedTerminator{
|
|
newTerminator("a", 10),
|
|
newTerminator("b", 20),
|
|
newTerminator("c", 30),
|
|
newTerminator("d", 40),
|
|
newTerminator("e", 50),
|
|
}
|
|
|
|
counts := map[string]int{}
|
|
iterations := 50000
|
|
|
|
for range iterations {
|
|
selected, _, err := s.Select(nil, terminators)
|
|
req.NoError(err)
|
|
counts[selected.GetId()]++
|
|
}
|
|
|
|
// Every terminator must be selected at least once
|
|
for _, id := range []string{"a", "b", "c", "d", "e"} {
|
|
req.Greater(counts[id], 0, "terminator %s was never selected", id)
|
|
}
|
|
|
|
// Lower cost terminators should be selected more often
|
|
req.Greater(counts["a"], counts["e"],
|
|
"lowest cost terminator should be selected more than highest cost: a=%d, e=%d",
|
|
counts["a"], counts["e"])
|
|
}
|