You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
4.2 KiB
Go
179 lines
4.2 KiB
Go
4 years ago
|
package internal
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"sort"
|
||
|
"strconv"
|
||
|
"testing"
|
||
|
|
||
|
"zero/core/mathx"
|
||
|
|
||
|
"github.com/golang/mock/gomock"
|
||
|
"github.com/stretchr/testify/assert"
|
||
|
)
|
||
|
|
||
|
func TestConsistent_addConn(t *testing.T) {
|
||
|
b := NewConsistentBalancer(func(server string) (interface{}, error) {
|
||
|
return mockConn{
|
||
|
server: server,
|
||
|
}, nil
|
||
|
}, func(server string, conn interface{}) error {
|
||
|
return errors.New("error")
|
||
|
}, func(kv KV) string {
|
||
|
return kv.Key
|
||
|
})
|
||
|
assert.Nil(t, b.AddConn(KV{
|
||
|
Key: "thekey1",
|
||
|
Val: "thevalue",
|
||
|
}))
|
||
|
assert.EqualValues(t, map[string]interface{}{
|
||
|
"thekey1": mockConn{server: "thevalue"},
|
||
|
}, b.conns)
|
||
|
assert.EqualValues(t, map[string][]string{
|
||
|
"thevalue": {"thekey1"},
|
||
|
}, b.servers)
|
||
|
assert.EqualValues(t, map[string]string{
|
||
|
"thekey1": "thevalue",
|
||
|
}, b.mapping)
|
||
|
assert.Nil(t, b.AddConn(KV{
|
||
|
Key: "thekey2",
|
||
|
Val: "thevalue",
|
||
|
}))
|
||
|
assert.EqualValues(t, map[string]interface{}{
|
||
|
"thekey1": mockConn{server: "thevalue"},
|
||
|
"thekey2": mockConn{server: "thevalue"},
|
||
|
}, b.conns)
|
||
|
assert.EqualValues(t, map[string][]string{
|
||
|
"thevalue": {"thekey1", "thekey2"},
|
||
|
}, b.servers)
|
||
|
assert.EqualValues(t, map[string]string{
|
||
|
"thekey1": "thevalue",
|
||
|
"thekey2": "thevalue",
|
||
|
}, b.mapping)
|
||
|
assert.False(t, b.IsEmpty())
|
||
|
|
||
|
b.RemoveKey("thekey1")
|
||
|
assert.EqualValues(t, map[string]interface{}{
|
||
|
"thekey2": mockConn{server: "thevalue"},
|
||
|
}, b.conns)
|
||
|
assert.EqualValues(t, map[string][]string{
|
||
|
"thevalue": {"thekey2"},
|
||
|
}, b.servers)
|
||
|
assert.EqualValues(t, map[string]string{
|
||
|
"thekey2": "thevalue",
|
||
|
}, b.mapping)
|
||
|
assert.False(t, b.IsEmpty())
|
||
|
|
||
|
b.RemoveKey("thekey2")
|
||
|
assert.Equal(t, 0, len(b.conns))
|
||
|
assert.EqualValues(t, map[string][]string{}, b.servers)
|
||
|
assert.EqualValues(t, map[string]string{}, b.mapping)
|
||
|
assert.True(t, b.IsEmpty())
|
||
|
}
|
||
|
|
||
|
func TestConsistent_addConnError(t *testing.T) {
|
||
|
b := NewConsistentBalancer(func(server string) (interface{}, error) {
|
||
|
return nil, errors.New("error")
|
||
|
}, func(server string, conn interface{}) error {
|
||
|
return nil
|
||
|
}, func(kv KV) string {
|
||
|
return kv.Key
|
||
|
})
|
||
|
assert.NotNil(t, b.AddConn(KV{
|
||
|
Key: "thekey1",
|
||
|
Val: "thevalue",
|
||
|
}))
|
||
|
assert.Equal(t, 0, len(b.conns))
|
||
|
assert.EqualValues(t, map[string][]string{}, b.servers)
|
||
|
assert.EqualValues(t, map[string]string{}, b.mapping)
|
||
|
}
|
||
|
|
||
|
func TestConsistent_next(t *testing.T) {
|
||
|
b := NewConsistentBalancer(func(server string) (interface{}, error) {
|
||
|
return mockConn{
|
||
|
server: server,
|
||
|
}, nil
|
||
|
}, func(server string, conn interface{}) error {
|
||
|
return errors.New("error")
|
||
|
}, func(kv KV) string {
|
||
|
return kv.Key
|
||
|
})
|
||
|
b.initialize()
|
||
|
|
||
|
_, ok := b.Next("any")
|
||
|
assert.False(t, ok)
|
||
|
|
||
|
const size = 100
|
||
|
for i := 0; i < size; i++ {
|
||
|
assert.Nil(t, b.AddConn(KV{
|
||
|
Key: "thekey/" + strconv.Itoa(i),
|
||
|
Val: "thevalue/" + strconv.Itoa(i),
|
||
|
}))
|
||
|
}
|
||
|
|
||
|
m := make(map[interface{}]int)
|
||
|
const total = 10000
|
||
|
for i := 0; i < total; i++ {
|
||
|
val, ok := b.Next(strconv.Itoa(i))
|
||
|
assert.True(t, ok)
|
||
|
m[val]++
|
||
|
}
|
||
|
|
||
|
entropy := mathx.CalcEntropy(m, total)
|
||
|
assert.Equal(t, size, len(m))
|
||
|
assert.True(t, entropy > .95)
|
||
|
|
||
|
for i := 0; i < size; i++ {
|
||
|
b.RemoveKey("thekey/" + strconv.Itoa(i))
|
||
|
}
|
||
|
_, ok = b.Next()
|
||
|
assert.False(t, ok)
|
||
|
}
|
||
|
|
||
|
func TestConsistentBalancer_Listener(t *testing.T) {
|
||
|
ctrl := gomock.NewController(t)
|
||
|
defer ctrl.Finish()
|
||
|
b := NewConsistentBalancer(func(server string) (interface{}, error) {
|
||
|
return mockConn{
|
||
|
server: server,
|
||
|
}, nil
|
||
|
}, func(server string, conn interface{}) error {
|
||
|
return nil
|
||
|
}, func(kv KV) string {
|
||
|
return kv.Key
|
||
|
})
|
||
|
assert.Nil(t, b.AddConn(KV{
|
||
|
Key: "key1",
|
||
|
Val: "val1",
|
||
|
}))
|
||
|
assert.Nil(t, b.AddConn(KV{
|
||
|
Key: "key2",
|
||
|
Val: "val2",
|
||
|
}))
|
||
|
|
||
|
listener := NewMockListener(ctrl)
|
||
|
listener.EXPECT().OnUpdate(gomock.Any(), gomock.Any(), "key2").Do(func(keys, vals, _ interface{}) {
|
||
|
sort.Strings(keys.([]string))
|
||
|
sort.Strings(vals.([]string))
|
||
|
assert.EqualValues(t, []string{"key1", "key2"}, keys)
|
||
|
assert.EqualValues(t, []string{"val1", "val2"}, vals)
|
||
|
})
|
||
|
b.setListener(listener)
|
||
|
b.notify("key2")
|
||
|
}
|
||
|
|
||
|
func TestConsistentBalancer_remove(t *testing.T) {
|
||
|
b := NewConsistentBalancer(func(server string) (interface{}, error) {
|
||
|
return mockConn{
|
||
|
server: server,
|
||
|
}, nil
|
||
|
}, func(server string, conn interface{}) error {
|
||
|
return nil
|
||
|
}, func(kv KV) string {
|
||
|
return kv.Key
|
||
|
})
|
||
|
|
||
|
assert.Nil(t, b.handlePrevious(nil))
|
||
|
assert.Nil(t, b.handlePrevious([]string{"any"}))
|
||
|
}
|