mirror of
https://source.quilibrium.com/quilibrium/ceremonyclient.git
synced 2024-12-27 17:15:18 +00:00
339 lines
11 KiB
Go
339 lines
11 KiB
Go
|
package connmgr
|
||
|
|
||
|
import (
|
||
|
"os"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/libp2p/go-libp2p/core/connmgr"
|
||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||
|
tu "github.com/libp2p/go-libp2p/core/test"
|
||
|
|
||
|
"github.com/benbjohnson/clock"
|
||
|
"github.com/stretchr/testify/require"
|
||
|
)
|
||
|
|
||
|
const TestResolution = 50 * time.Millisecond
|
||
|
|
||
|
func waitForTag(t *testing.T, mgr *BasicConnMgr, id peer.ID) {
|
||
|
t.Helper()
|
||
|
require.Eventually(t, func() bool { return mgr.GetTagInfo(id) != nil }, 500*time.Millisecond, 10*time.Millisecond)
|
||
|
}
|
||
|
|
||
|
func TestDecayExpire(t *testing.T) {
|
||
|
id := tu.RandPeerIDFatal(t)
|
||
|
mgr, decay, mockClock := testDecayTracker(t)
|
||
|
|
||
|
tag, err := decay.RegisterDecayingTag("pop", 250*time.Millisecond, connmgr.DecayExpireWhenInactive(1*time.Second), connmgr.BumpSumUnbounded())
|
||
|
require.NoError(t, err)
|
||
|
require.NoError(t, tag.Bump(id, 10))
|
||
|
|
||
|
waitForTag(t, mgr, id)
|
||
|
require.Equal(t, 10, mgr.GetTagInfo(id).Value)
|
||
|
|
||
|
mockClock.Add(250 * time.Millisecond)
|
||
|
mockClock.Add(250 * time.Millisecond)
|
||
|
mockClock.Add(250 * time.Millisecond)
|
||
|
mockClock.Add(250 * time.Millisecond)
|
||
|
|
||
|
eventuallyEqual(t, func() int { return mgr.GetTagInfo(id).Value }, 0)
|
||
|
}
|
||
|
|
||
|
func TestMultipleBumps(t *testing.T) {
|
||
|
id := tu.RandPeerIDFatal(t)
|
||
|
mgr, decay, _ := testDecayTracker(t)
|
||
|
|
||
|
tag, err := decay.RegisterDecayingTag("pop", 250*time.Millisecond, connmgr.DecayExpireWhenInactive(1*time.Second), connmgr.BumpSumBounded(10, 20))
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
require.NoError(t, tag.Bump(id, 5))
|
||
|
|
||
|
waitForTag(t, mgr, id)
|
||
|
require.Equal(t, mgr.GetTagInfo(id).Value, 10)
|
||
|
|
||
|
require.NoError(t, tag.Bump(id, 100))
|
||
|
require.Eventually(t, func() bool { return mgr.GetTagInfo(id).Value == 20 }, 100*time.Millisecond, 10*time.Millisecond, "expected tag value to decay to 20")
|
||
|
}
|
||
|
|
||
|
func TestMultipleTagsNoDecay(t *testing.T) {
|
||
|
id := tu.RandPeerIDFatal(t)
|
||
|
mgr, decay, _ := testDecayTracker(t)
|
||
|
|
||
|
tag1, err := decay.RegisterDecayingTag("beep", 250*time.Millisecond, connmgr.DecayNone(), connmgr.BumpSumBounded(0, 100))
|
||
|
require.NoError(t, err)
|
||
|
tag2, err := decay.RegisterDecayingTag("bop", 250*time.Millisecond, connmgr.DecayNone(), connmgr.BumpSumBounded(0, 100))
|
||
|
require.NoError(t, err)
|
||
|
tag3, err := decay.RegisterDecayingTag("foo", 250*time.Millisecond, connmgr.DecayNone(), connmgr.BumpSumBounded(0, 100))
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
_ = tag1.Bump(id, 100)
|
||
|
_ = tag2.Bump(id, 100)
|
||
|
_ = tag3.Bump(id, 100)
|
||
|
_ = tag1.Bump(id, 100)
|
||
|
_ = tag2.Bump(id, 100)
|
||
|
_ = tag3.Bump(id, 100)
|
||
|
|
||
|
waitForTag(t, mgr, id)
|
||
|
|
||
|
// all tags are upper-bounded, so the score must be 300
|
||
|
ti := mgr.GetTagInfo(id)
|
||
|
require.Equal(t, ti.Value, 300)
|
||
|
|
||
|
for _, s := range []string{"beep", "bop", "foo"} {
|
||
|
if v, ok := ti.Tags[s]; !ok || v != 100 {
|
||
|
t.Fatalf("expected tag %s to be 100; was = %d", s, v)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCustomFunctions(t *testing.T) {
|
||
|
id := tu.RandPeerIDFatal(t)
|
||
|
mgr, decay, mockClock := testDecayTracker(t)
|
||
|
|
||
|
tag1, err := decay.RegisterDecayingTag("beep", 250*time.Millisecond, connmgr.DecayFixed(10), connmgr.BumpSumUnbounded())
|
||
|
require.NoError(t, err)
|
||
|
tag2, err := decay.RegisterDecayingTag("bop", 100*time.Millisecond, connmgr.DecayFixed(5), connmgr.BumpSumUnbounded())
|
||
|
require.NoError(t, err)
|
||
|
tag3, err := decay.RegisterDecayingTag("foo", 50*time.Millisecond, connmgr.DecayFixed(1), connmgr.BumpSumUnbounded())
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
_ = tag1.Bump(id, 1000)
|
||
|
_ = tag2.Bump(id, 1000)
|
||
|
_ = tag3.Bump(id, 1000)
|
||
|
|
||
|
waitForTag(t, mgr, id)
|
||
|
|
||
|
// no decay has occurred yet, so score must be 3000.
|
||
|
require.Equal(t, 3000, mgr.GetTagInfo(id).Value)
|
||
|
|
||
|
// only tag3 should tick.
|
||
|
mockClock.Add(50 * time.Millisecond)
|
||
|
eventuallyEqual(t, func() int { return mgr.GetTagInfo(id).Value }, 2999)
|
||
|
|
||
|
// tag3 will tick thrice, tag2 will tick twice.
|
||
|
mockClock.Add(150 * time.Millisecond)
|
||
|
eventuallyEqual(t, func() int { return mgr.GetTagInfo(id).Value }, 2986)
|
||
|
|
||
|
// tag3 will tick once, tag1 will tick once.
|
||
|
mockClock.Add(50 * time.Millisecond)
|
||
|
eventuallyEqual(t, func() int { return mgr.GetTagInfo(id).Value }, 2975)
|
||
|
}
|
||
|
|
||
|
func TestMultiplePeers(t *testing.T) {
|
||
|
ids := []peer.ID{tu.RandPeerIDFatal(t), tu.RandPeerIDFatal(t), tu.RandPeerIDFatal(t)}
|
||
|
mgr, decay, mockClock := testDecayTracker(t)
|
||
|
|
||
|
tag1, err := decay.RegisterDecayingTag("beep", 250*time.Millisecond, connmgr.DecayFixed(10), connmgr.BumpSumUnbounded())
|
||
|
require.NoError(t, err)
|
||
|
tag2, err := decay.RegisterDecayingTag("bop", 100*time.Millisecond, connmgr.DecayFixed(5), connmgr.BumpSumUnbounded())
|
||
|
require.NoError(t, err)
|
||
|
tag3, err := decay.RegisterDecayingTag("foo", 50*time.Millisecond, connmgr.DecayFixed(1), connmgr.BumpSumUnbounded())
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
_ = tag1.Bump(ids[0], 1000)
|
||
|
_ = tag2.Bump(ids[0], 1000)
|
||
|
_ = tag3.Bump(ids[0], 1000)
|
||
|
|
||
|
_ = tag1.Bump(ids[1], 500)
|
||
|
_ = tag2.Bump(ids[1], 500)
|
||
|
_ = tag3.Bump(ids[1], 500)
|
||
|
|
||
|
_ = tag1.Bump(ids[2], 100)
|
||
|
_ = tag2.Bump(ids[2], 100)
|
||
|
_ = tag3.Bump(ids[2], 100)
|
||
|
|
||
|
// allow the background goroutine to process bumps.
|
||
|
waitFor := 100 * time.Millisecond
|
||
|
tick := 10 * time.Millisecond
|
||
|
if os.Getenv("CI") != "" {
|
||
|
waitFor *= 10
|
||
|
tick *= 10
|
||
|
}
|
||
|
|
||
|
require.Eventually(t, func() bool {
|
||
|
return mgr.GetTagInfo(ids[0]) != nil && mgr.GetTagInfo(ids[1]) != nil && mgr.GetTagInfo(ids[2]) != nil
|
||
|
}, waitFor, tick)
|
||
|
|
||
|
mockClock.Add(3 * time.Second)
|
||
|
|
||
|
waitFor = 500 * time.Millisecond
|
||
|
if os.Getenv("CI") != "" {
|
||
|
waitFor *= 10
|
||
|
}
|
||
|
|
||
|
require.Eventually(t, func() bool { return mgr.GetTagInfo(ids[0]).Value == 2670 }, waitFor, tick)
|
||
|
require.Equal(t, 1170, mgr.GetTagInfo(ids[1]).Value)
|
||
|
require.Equal(t, 40, mgr.GetTagInfo(ids[2]).Value)
|
||
|
}
|
||
|
|
||
|
func eventuallyEqual(t *testing.T, f func() int, val int) {
|
||
|
t.Helper()
|
||
|
require.Eventually(t, func() bool {
|
||
|
v := f()
|
||
|
if v == val {
|
||
|
return true
|
||
|
}
|
||
|
t.Log("f() was", v, "expected", val, "retrying...")
|
||
|
return false
|
||
|
}, 1*time.Second, 10*time.Millisecond)
|
||
|
}
|
||
|
|
||
|
func TestLinearDecayOverwrite(t *testing.T) {
|
||
|
id := tu.RandPeerIDFatal(t)
|
||
|
mgr, decay, mockClock := testDecayTracker(t)
|
||
|
|
||
|
tag1, err := decay.RegisterDecayingTag("beep", 250*time.Millisecond, connmgr.DecayLinear(0.5), connmgr.BumpOverwrite())
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
_ = tag1.Bump(id, 1000)
|
||
|
waitForTag(t, mgr, id)
|
||
|
|
||
|
eventuallyEqual(t, func() int { return mgr.GetTagInfo(id).Value }, 1000)
|
||
|
mockClock.Add(250 * time.Millisecond)
|
||
|
eventuallyEqual(t, func() int { return mgr.GetTagInfo(id).Value }, 500)
|
||
|
|
||
|
mockClock.Add(250 * time.Millisecond)
|
||
|
eventuallyEqual(t, func() int { return mgr.GetTagInfo(id).Value }, 250)
|
||
|
|
||
|
_ = tag1.Bump(id, 1000)
|
||
|
eventuallyEqual(t, func() int { return mgr.GetTagInfo(id).Value }, 1000)
|
||
|
}
|
||
|
|
||
|
func TestResolutionMisaligned(t *testing.T) {
|
||
|
var (
|
||
|
id = tu.RandPeerIDFatal(t)
|
||
|
mgr, decay, mockClock = testDecayTracker(t)
|
||
|
require = require.New(t)
|
||
|
)
|
||
|
|
||
|
tag1, err := decay.RegisterDecayingTag("beep", time.Duration(float64(TestResolution)*1.4), connmgr.DecayFixed(1), connmgr.BumpOverwrite())
|
||
|
require.NoError(err)
|
||
|
|
||
|
tag2, err := decay.RegisterDecayingTag("bop", time.Duration(float64(TestResolution)*2.4), connmgr.DecayFixed(1), connmgr.BumpOverwrite())
|
||
|
require.NoError(err)
|
||
|
|
||
|
_ = tag1.Bump(id, 1000)
|
||
|
_ = tag2.Bump(id, 1000)
|
||
|
// allow the background goroutine to process bumps.
|
||
|
<-time.After(500 * time.Millisecond)
|
||
|
|
||
|
// first tick.
|
||
|
mockClock.Add(TestResolution)
|
||
|
eventuallyEqual(t, func() int { return mgr.GetTagInfo(id).Tags["beep"] }, 1000)
|
||
|
require.Equal(1000, mgr.GetTagInfo(id).Tags["bop"])
|
||
|
|
||
|
// next tick; tag1 would've ticked.
|
||
|
mockClock.Add(TestResolution)
|
||
|
eventuallyEqual(t, func() int { return mgr.GetTagInfo(id).Tags["beep"] }, 999)
|
||
|
require.Equal(1000, mgr.GetTagInfo(id).Tags["bop"])
|
||
|
|
||
|
// next tick; tag1 would've ticked twice, tag2 once.
|
||
|
mockClock.Add(TestResolution)
|
||
|
eventuallyEqual(t, func() int { return mgr.GetTagInfo(id).Tags["beep"] }, 998)
|
||
|
require.Equal(999, mgr.GetTagInfo(id).Tags["bop"])
|
||
|
|
||
|
require.Equal(1997, mgr.GetTagInfo(id).Value)
|
||
|
}
|
||
|
|
||
|
func TestTagRemoval(t *testing.T) {
|
||
|
id1, id2 := tu.RandPeerIDFatal(t), tu.RandPeerIDFatal(t)
|
||
|
mgr, decay, mockClock := testDecayTracker(t)
|
||
|
|
||
|
tag1, err := decay.RegisterDecayingTag("beep", TestResolution, connmgr.DecayFixed(1), connmgr.BumpOverwrite())
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
tag2, err := decay.RegisterDecayingTag("bop", TestResolution, connmgr.DecayFixed(1), connmgr.BumpOverwrite())
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
// id1 has both tags; id2 only has the first tag.
|
||
|
_ = tag1.Bump(id1, 1000)
|
||
|
_ = tag2.Bump(id1, 1000)
|
||
|
_ = tag1.Bump(id2, 1000)
|
||
|
|
||
|
waitForTag(t, mgr, id1)
|
||
|
waitForTag(t, mgr, id2)
|
||
|
|
||
|
// first tick.
|
||
|
mockClock.Add(TestResolution)
|
||
|
eventuallyEqual(t, func() int { return mgr.GetTagInfo(id1).Tags["beep"] }, 999)
|
||
|
require.Equal(t, 999, mgr.GetTagInfo(id1).Tags["bop"])
|
||
|
require.Equal(t, 999, mgr.GetTagInfo(id2).Tags["beep"])
|
||
|
|
||
|
require.Equal(t, 999*2, mgr.GetTagInfo(id1).Value)
|
||
|
require.Equal(t, 999, mgr.GetTagInfo(id2).Value)
|
||
|
|
||
|
// remove tag1 from p1.
|
||
|
require.NoError(t, tag1.Remove(id1))
|
||
|
|
||
|
// next tick. both peers only have 1 tag, both at 998 value.
|
||
|
mockClock.Add(TestResolution)
|
||
|
require.Eventually(t, func() bool { return mgr.GetTagInfo(id1).Tags["beep"] == 0 }, 500*time.Millisecond, 10*time.Millisecond)
|
||
|
require.Equal(t, 998, mgr.GetTagInfo(id1).Tags["bop"])
|
||
|
require.Equal(t, 998, mgr.GetTagInfo(id2).Tags["beep"])
|
||
|
|
||
|
require.Equal(t, 998, mgr.GetTagInfo(id1).Value)
|
||
|
require.Equal(t, 998, mgr.GetTagInfo(id2).Value)
|
||
|
|
||
|
// remove tag1 from p1 again; no error.
|
||
|
require.NoError(t, tag1.Remove(id1))
|
||
|
}
|
||
|
|
||
|
func TestTagClosure(t *testing.T) {
|
||
|
id := tu.RandPeerIDFatal(t)
|
||
|
mgr, decay, mockClock := testDecayTracker(t)
|
||
|
|
||
|
tag1, err := decay.RegisterDecayingTag("beep", TestResolution, connmgr.DecayFixed(1), connmgr.BumpOverwrite())
|
||
|
require.NoError(t, err)
|
||
|
tag2, err := decay.RegisterDecayingTag("bop", TestResolution, connmgr.DecayFixed(1), connmgr.BumpOverwrite())
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
_ = tag1.Bump(id, 1000)
|
||
|
_ = tag2.Bump(id, 1000)
|
||
|
waitForTag(t, mgr, id)
|
||
|
|
||
|
// nothing has happened.
|
||
|
mockClock.Add(TestResolution)
|
||
|
eventuallyEqual(t, func() int { return mgr.GetTagInfo(id).Tags["beep"] }, 999)
|
||
|
require.Equal(t, 999, mgr.GetTagInfo(id).Tags["bop"])
|
||
|
require.Equal(t, 999*2, mgr.GetTagInfo(id).Value)
|
||
|
|
||
|
// next tick; tag1 would've ticked.
|
||
|
mockClock.Add(TestResolution)
|
||
|
eventuallyEqual(t, func() int { return mgr.GetTagInfo(id).Tags["beep"] }, 998)
|
||
|
require.Equal(t, 998, mgr.GetTagInfo(id).Tags["bop"])
|
||
|
require.Equal(t, 998*2, mgr.GetTagInfo(id).Value)
|
||
|
|
||
|
// close the tag.
|
||
|
require.NoError(t, tag1.Close())
|
||
|
|
||
|
// allow the background goroutine to process the closure.
|
||
|
require.Eventually(t, func() bool { return mgr.GetTagInfo(id).Value == 998 }, 500*time.Millisecond, 10*time.Millisecond)
|
||
|
|
||
|
// a second closure should not error.
|
||
|
require.NoError(t, tag1.Close())
|
||
|
|
||
|
// bumping a tag after it's been closed should error.
|
||
|
require.Error(t, tag1.Bump(id, 5))
|
||
|
}
|
||
|
|
||
|
func testDecayTracker(tb testing.TB) (*BasicConnMgr, connmgr.Decayer, *clock.Mock) {
|
||
|
mockClock := clock.NewMock()
|
||
|
cfg := &DecayerCfg{
|
||
|
Resolution: TestResolution,
|
||
|
Clock: mockClock,
|
||
|
}
|
||
|
|
||
|
mgr, err := NewConnManager(10, 10, WithGracePeriod(time.Second), DecayerConfig(cfg))
|
||
|
require.NoError(tb, err)
|
||
|
decay, ok := connmgr.SupportsDecay(mgr)
|
||
|
if !ok {
|
||
|
tb.Fatalf("connmgr does not support decay")
|
||
|
}
|
||
|
tb.Cleanup(func() {
|
||
|
mgr.Close()
|
||
|
decay.Close()
|
||
|
})
|
||
|
|
||
|
return mgr, decay, mockClock
|
||
|
}
|