package serf
|
|
import (
|
"reflect"
|
"sort"
|
"testing"
|
"time"
|
)
|
|
func TestMemberEventCoalesce_Basic(t *testing.T) {
|
outCh := make(chan Event, 64)
|
shutdownCh := make(chan struct{})
|
defer close(shutdownCh)
|
|
c := &memberEventCoalescer{
|
lastEvents: make(map[string]EventType),
|
latestEvents: make(map[string]coalesceEvent),
|
}
|
|
inCh := coalescedEventCh(outCh, shutdownCh,
|
5*time.Millisecond, 5*time.Millisecond, c)
|
|
send := []Event{
|
MemberEvent{
|
Type: EventMemberJoin,
|
Members: []Member{Member{Name: "foo"}},
|
},
|
MemberEvent{
|
Type: EventMemberLeave,
|
Members: []Member{Member{Name: "foo"}},
|
},
|
MemberEvent{
|
Type: EventMemberLeave,
|
Members: []Member{Member{Name: "bar"}},
|
},
|
MemberEvent{
|
Type: EventMemberUpdate,
|
Members: []Member{Member{Name: "zip", Tags: map[string]string{"role": "foo"}}},
|
},
|
MemberEvent{
|
Type: EventMemberUpdate,
|
Members: []Member{Member{Name: "zip", Tags: map[string]string{"role": "bar"}}},
|
},
|
MemberEvent{
|
Type: EventMemberReap,
|
Members: []Member{Member{Name: "dead"}},
|
},
|
}
|
|
for _, e := range send {
|
inCh <- e
|
}
|
|
events := make(map[EventType]Event)
|
timeout := time.After(10 * time.Millisecond)
|
|
MEMBEREVENTFORLOOP:
|
for {
|
select {
|
case e := <-outCh:
|
events[e.EventType()] = e
|
case <-timeout:
|
break MEMBEREVENTFORLOOP
|
}
|
}
|
|
if len(events) != 3 {
|
t.Fatalf("bad: %#v", events)
|
}
|
|
if e, ok := events[EventMemberLeave]; !ok {
|
t.Fatalf("bad: %#v", events)
|
} else {
|
me := e.(MemberEvent)
|
|
if len(me.Members) != 2 {
|
t.Fatalf("bad: %#v", me)
|
}
|
|
expected := []string{"bar", "foo"}
|
names := []string{me.Members[0].Name, me.Members[1].Name}
|
sort.Strings(names)
|
|
if !reflect.DeepEqual(expected, names) {
|
t.Fatalf("bad: %#v", names)
|
}
|
}
|
|
if e, ok := events[EventMemberUpdate]; !ok {
|
t.Fatalf("bad: %#v", events)
|
} else {
|
me := e.(MemberEvent)
|
if len(me.Members) != 1 {
|
t.Fatalf("bad: %#v", me)
|
}
|
|
if me.Members[0].Name != "zip" {
|
t.Fatalf("bad: %#v", me.Members)
|
}
|
if me.Members[0].Tags["role"] != "bar" {
|
t.Fatalf("bad: %#v", me.Members[0].Tags)
|
}
|
}
|
|
if e, ok := events[EventMemberReap]; !ok {
|
t.Fatalf("bad: %#v", events)
|
} else {
|
me := e.(MemberEvent)
|
if len(me.Members) != 1 {
|
t.Fatalf("bad: %#v", me)
|
}
|
|
if me.Members[0].Name != "dead" {
|
t.Fatalf("bad: %#v", me.Members)
|
}
|
}
|
}
|
|
func TestMemberEventCoalesce_TagUpdate(t *testing.T) {
|
outCh := make(chan Event, 64)
|
shutdownCh := make(chan struct{})
|
defer close(shutdownCh)
|
|
c := &memberEventCoalescer{
|
lastEvents: make(map[string]EventType),
|
latestEvents: make(map[string]coalesceEvent),
|
}
|
|
inCh := coalescedEventCh(outCh, shutdownCh,
|
5*time.Millisecond, 5*time.Millisecond, c)
|
|
inCh <- MemberEvent{
|
Type: EventMemberUpdate,
|
Members: []Member{Member{Name: "foo", Tags: map[string]string{"role": "foo"}}},
|
}
|
|
time.Sleep(10 * time.Millisecond)
|
|
select {
|
case e := <-outCh:
|
if e.EventType() != EventMemberUpdate {
|
t.Fatalf("expected update")
|
}
|
default:
|
t.Fatalf("expected update")
|
}
|
|
// Second update should not be suppressed even though
|
// last event was an update
|
inCh <- MemberEvent{
|
Type: EventMemberUpdate,
|
Members: []Member{Member{Name: "foo", Tags: map[string]string{"role": "bar"}}},
|
}
|
time.Sleep(10 * time.Millisecond)
|
|
select {
|
case e := <-outCh:
|
if e.EventType() != EventMemberUpdate {
|
t.Fatalf("expected update")
|
}
|
default:
|
t.Fatalf("expected update")
|
}
|
}
|
|
func TestMemberEventCoalesce_passThrough(t *testing.T) {
|
cases := []struct {
|
e Event
|
handle bool
|
}{
|
{UserEvent{}, false},
|
{MemberEvent{Type: EventMemberJoin}, true},
|
{MemberEvent{Type: EventMemberLeave}, true},
|
{MemberEvent{Type: EventMemberFailed}, true},
|
{MemberEvent{Type: EventMemberUpdate}, true},
|
{MemberEvent{Type: EventMemberReap}, true},
|
}
|
|
for _, tc := range cases {
|
c := &memberEventCoalescer{}
|
if tc.handle != c.Handle(tc.e) {
|
t.Fatalf("bad: %#v", tc.e)
|
}
|
}
|
}
|