mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
196 lines
4.2 KiB
Go
196 lines
4.2 KiB
Go
package raft
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
const (
|
|
defaultHeartbeat = 1
|
|
defaultElection = 5
|
|
)
|
|
|
|
func TestTickMsgHup(t *testing.T) {
|
|
n := New(0, defaultHeartbeat, defaultElection)
|
|
n.sm = newStateMachine(0, []int64{0, 1, 2})
|
|
// simulate to patch the join log
|
|
n.Step(Message{Type: msgApp, Commit: 1, Entries: []Entry{Entry{}}})
|
|
|
|
for i := 0; i < defaultElection*2; i++ {
|
|
n.Tick()
|
|
}
|
|
|
|
called := false
|
|
for _, m := range n.Msgs() {
|
|
if m.Type == msgVote {
|
|
called = true
|
|
}
|
|
}
|
|
|
|
if !called {
|
|
t.Errorf("called = %v, want true", called)
|
|
}
|
|
}
|
|
|
|
func TestTickMsgBeat(t *testing.T) {
|
|
k := 3
|
|
n := dictate(New(0, defaultHeartbeat, defaultElection))
|
|
n.Next()
|
|
for i := 1; i < k; i++ {
|
|
n.Add(int64(i), "", nil)
|
|
for _, m := range n.Msgs() {
|
|
if m.Type == msgApp {
|
|
n.Step(Message{From: m.To, Type: msgAppResp, Index: m.Index + int64(len(m.Entries))})
|
|
}
|
|
}
|
|
// ignore commit index update messages
|
|
n.Msgs()
|
|
n.Next()
|
|
}
|
|
|
|
for i := 0; i < defaultHeartbeat+1; i++ {
|
|
n.Tick()
|
|
}
|
|
|
|
called := 0
|
|
for _, m := range n.Msgs() {
|
|
if m.Type == msgApp {
|
|
called++
|
|
}
|
|
}
|
|
|
|
// msgBeat -> k-1 append
|
|
w := k - 1
|
|
if called != w {
|
|
t.Errorf("called = %v, want %v", called, w)
|
|
}
|
|
}
|
|
|
|
func TestResetElapse(t *testing.T) {
|
|
tests := []struct {
|
|
msg Message
|
|
welapsed tick
|
|
}{
|
|
{Message{From: 0, To: 1, Type: msgApp, Term: 2, Entries: []Entry{{Term: 1}}}, 0},
|
|
{Message{From: 0, To: 1, Type: msgApp, Term: 1, Entries: []Entry{{Term: 1}}}, 1},
|
|
{Message{From: 0, To: 1, Type: msgVote, Term: 2, Index: 1, LogTerm: 1}, 0},
|
|
{Message{From: 0, To: 1, Type: msgVote, Term: 1}, 1},
|
|
}
|
|
|
|
for i, tt := range tests {
|
|
n := New(0, defaultHeartbeat, defaultElection)
|
|
n.sm = newStateMachine(0, []int64{0, 1, 2})
|
|
n.sm.log.append(0, Entry{Type: Normal, Term: 1})
|
|
n.sm.term = 2
|
|
n.sm.log.committed = 1
|
|
|
|
n.Tick()
|
|
if n.elapsed != 1 {
|
|
t.Errorf("%d: elpased = %d, want %d", i, n.elapsed, 1)
|
|
}
|
|
|
|
n.Step(tt.msg)
|
|
if n.elapsed != tt.welapsed {
|
|
t.Errorf("%d: elpased = %d, want %d", i, n.elapsed, tt.welapsed)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestStartCluster(t *testing.T) {
|
|
n := dictate(New(0, defaultHeartbeat, defaultElection))
|
|
n.Next()
|
|
|
|
if len(n.sm.ins) != 1 {
|
|
t.Errorf("k = %d, want 1", len(n.sm.ins))
|
|
}
|
|
if n.sm.id != 0 {
|
|
t.Errorf("id = %d, want 0", n.sm.id)
|
|
}
|
|
if n.sm.state != stateLeader {
|
|
t.Errorf("state = %s, want %s", n.sm.state, stateLeader)
|
|
}
|
|
}
|
|
|
|
func TestAdd(t *testing.T) {
|
|
n := dictate(New(0, defaultHeartbeat, defaultElection))
|
|
n.Next()
|
|
n.Add(1, "", nil)
|
|
n.Next()
|
|
|
|
if len(n.sm.ins) != 2 {
|
|
t.Errorf("k = %d, want 2", len(n.sm.ins))
|
|
}
|
|
if n.sm.id != 0 {
|
|
t.Errorf("id = %d, want 0", n.sm.id)
|
|
}
|
|
}
|
|
|
|
func TestRemove(t *testing.T) {
|
|
n := dictate(New(0, defaultHeartbeat, defaultElection))
|
|
n.Next()
|
|
n.Add(1, "", nil)
|
|
n.Next()
|
|
n.Remove(0)
|
|
n.Step(Message{Type: msgAppResp, From: 1, Term: 1, Index: 4})
|
|
n.Next()
|
|
|
|
if len(n.sm.ins) != 1 {
|
|
t.Errorf("k = %d, want 1", len(n.sm.ins))
|
|
}
|
|
if n.sm.id != 0 {
|
|
t.Errorf("id = %d, want 0", n.sm.id)
|
|
}
|
|
}
|
|
|
|
func TestDenial(t *testing.T) {
|
|
logents := []Entry{
|
|
{Type: AddNode, Term: 1, Data: []byte(`{"NodeId":1}`)},
|
|
{Type: AddNode, Term: 1, Data: []byte(`{"NodeId":2}`)},
|
|
{Type: RemoveNode, Term: 1, Data: []byte(`{"NodeId":2}`)},
|
|
}
|
|
|
|
tests := []struct {
|
|
ent Entry
|
|
wdenied map[int64]bool
|
|
}{
|
|
{
|
|
Entry{Type: AddNode, Term: 1, Data: []byte(`{"NodeId":2}`)},
|
|
map[int64]bool{0: false, 1: false, 2: false},
|
|
},
|
|
{
|
|
Entry{Type: RemoveNode, Term: 1, Data: []byte(`{"NodeId":1}`)},
|
|
map[int64]bool{0: false, 1: true, 2: true},
|
|
},
|
|
{
|
|
Entry{Type: RemoveNode, Term: 1, Data: []byte(`{"NodeId":0}`)},
|
|
map[int64]bool{0: true, 1: false, 2: true},
|
|
},
|
|
}
|
|
|
|
for i, tt := range tests {
|
|
n := dictate(New(0, defaultHeartbeat, defaultElection))
|
|
n.Next()
|
|
n.Msgs()
|
|
n.sm.log.append(n.sm.log.committed, append(logents, tt.ent)...)
|
|
n.sm.log.committed += int64(len(logents) + 1)
|
|
n.Next()
|
|
|
|
for id, denied := range tt.wdenied {
|
|
n.Step(Message{From: id, To: 0, Type: msgApp, Term: 1})
|
|
w := []Message{}
|
|
if denied {
|
|
w = []Message{{From: 0, To: id, Term: 1, Type: msgDenied}}
|
|
}
|
|
if g := n.Msgs(); !reflect.DeepEqual(g, w) {
|
|
t.Errorf("#%d: msgs for %d = %+v, want %+v", i, id, g, w)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func dictate(n *Node) *Node {
|
|
n.Step(Message{Type: msgHup})
|
|
n.Add(n.Id(), "", nil)
|
|
return n
|
|
}
|