rawnode_test.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. // Copyright 2015 CoreOS, Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package raft
  15. import (
  16. "bytes"
  17. "reflect"
  18. "testing"
  19. "github.com/coreos/etcd/raft/raftpb"
  20. )
  21. // TestRawNodeStep ensures that RawNode.Step ignore local message.
  22. func TestRawNodeStep(t *testing.T) {
  23. for i, msgn := range raftpb.MessageType_name {
  24. s := NewMemoryStorage()
  25. rawNode, err := NewRawNode(newTestConfig(1, nil, 10, 1, s), []Peer{{ID: 1}})
  26. if err != nil {
  27. t.Fatal(err)
  28. }
  29. msgt := raftpb.MessageType(i)
  30. err = rawNode.Step(raftpb.Message{Type: msgt})
  31. // LocalMsg should be ignored.
  32. if msgt == raftpb.MsgBeat || msgt == raftpb.MsgHup || msgt == raftpb.MsgUnreachable || msgt == raftpb.MsgSnapStatus {
  33. if err != ErrStepLocalMsg {
  34. t.Errorf("%d: step should ignore %s", msgt, msgn)
  35. }
  36. }
  37. }
  38. }
  39. // TestNodeStepUnblock from node_test.go has no equivalent in rawNode because there is
  40. // no goroutine in RawNode.
  41. // TestRawNodeProposeAndConfChange ensures that RawNode.Propose and RawNode.ProposeConfChange
  42. // send the given proposal and ConfChange to the underlying raft.
  43. func TestRawNodeProposeAndConfChange(t *testing.T) {
  44. s := NewMemoryStorage()
  45. var err error
  46. rawNode, err := NewRawNode(newTestConfig(1, nil, 10, 1, s), []Peer{{ID: 1}})
  47. if err != nil {
  48. t.Fatal(err)
  49. }
  50. rawNode.Campaign()
  51. proposed := false
  52. var lastIndex uint64
  53. var ccdata []byte
  54. for {
  55. rd := rawNode.Ready()
  56. s.Append(rd.Entries)
  57. // Once we are the leader, propose a command and a ConfChange.
  58. if !proposed && rd.SoftState.Lead == rawNode.raft.id {
  59. rawNode.Propose([]byte("somedata"))
  60. cc := raftpb.ConfChange{Type: raftpb.ConfChangeAddNode, NodeID: 1}
  61. ccdata, err = cc.Marshal()
  62. if err != nil {
  63. t.Fatal(err)
  64. }
  65. rawNode.ProposeConfChange(cc)
  66. proposed = true
  67. }
  68. rawNode.Advance(rd)
  69. // Exit when we have four entries: one ConfChange, one no-op for the election,
  70. // our proposed command and proposed ConfChange.
  71. lastIndex, err = s.LastIndex()
  72. if err != nil {
  73. t.Fatal(err)
  74. }
  75. if lastIndex >= 4 {
  76. break
  77. }
  78. }
  79. entries, err := s.Entries(lastIndex-1, lastIndex+1, noLimit)
  80. if err != nil {
  81. t.Fatal(err)
  82. }
  83. if len(entries) != 2 {
  84. t.Fatalf("len(entries) = %d, want %d", len(entries), 2)
  85. }
  86. if !bytes.Equal(entries[0].Data, []byte("somedata")) {
  87. t.Errorf("entries[0].Data = %v, want %v", entries[0].Data, []byte("somedata"))
  88. }
  89. if entries[1].Type != raftpb.EntryConfChange {
  90. t.Fatalf("type = %v, want %v", entries[1].Type, raftpb.EntryConfChange)
  91. }
  92. if !bytes.Equal(entries[1].Data, ccdata) {
  93. t.Errorf("data = %v, want %v", entries[1].Data, ccdata)
  94. }
  95. }
  96. // TestBlockProposal from node_test.go has no equivalent in rawNode because there is
  97. // no leader check in RawNode.
  98. // TestNodeTick from node_test.go has no equivalent in rawNode because
  99. // it reaches into the raft object which is not exposed.
  100. // TestNodeStop from node_test.go has no equivalent in rawNode because there is
  101. // no goroutine in RawNode.
  102. // TestRawNodeStart ensures that a node can be started correctly. The node should
  103. // start with correct configuration change entries, and can accept and commit
  104. // proposals.
  105. func TestRawNodeStart(t *testing.T) {
  106. cc := raftpb.ConfChange{Type: raftpb.ConfChangeAddNode, NodeID: 1}
  107. ccdata, err := cc.Marshal()
  108. if err != nil {
  109. t.Fatalf("unexpected marshal error: %v", err)
  110. }
  111. wants := []Ready{
  112. {
  113. SoftState: &SoftState{Lead: 1, RaftState: StateLeader},
  114. HardState: raftpb.HardState{Term: 2, Commit: 2, Vote: 1},
  115. Entries: []raftpb.Entry{
  116. {Type: raftpb.EntryConfChange, Term: 1, Index: 1, Data: ccdata},
  117. {Term: 2, Index: 2},
  118. },
  119. CommittedEntries: []raftpb.Entry{
  120. {Type: raftpb.EntryConfChange, Term: 1, Index: 1, Data: ccdata},
  121. {Term: 2, Index: 2},
  122. },
  123. },
  124. {
  125. HardState: raftpb.HardState{Term: 2, Commit: 3, Vote: 1},
  126. Entries: []raftpb.Entry{{Term: 2, Index: 3, Data: []byte("foo")}},
  127. CommittedEntries: []raftpb.Entry{{Term: 2, Index: 3, Data: []byte("foo")}},
  128. },
  129. }
  130. storage := NewMemoryStorage()
  131. rawNode, err := NewRawNode(newTestConfig(1, nil, 10, 1, storage), []Peer{{ID: 1}})
  132. if err != nil {
  133. t.Fatal(err)
  134. }
  135. rawNode.Campaign()
  136. rd := rawNode.Ready()
  137. t.Logf("rd %v", rd)
  138. if !reflect.DeepEqual(rd, wants[0]) {
  139. t.Fatalf("#%d: g = %+v,\n w %+v", 1, rd, wants[0])
  140. } else {
  141. storage.Append(rd.Entries)
  142. rawNode.Advance(rd)
  143. }
  144. rawNode.Propose([]byte("foo"))
  145. if rd = rawNode.Ready(); !reflect.DeepEqual(rd, wants[1]) {
  146. t.Errorf("#%d: g = %+v,\n w %+v", 2, rd, wants[1])
  147. } else {
  148. storage.Append(rd.Entries)
  149. rawNode.Advance(rd)
  150. }
  151. if rawNode.HasReady() {
  152. t.Errorf("unexpected Ready: %+v", rawNode.Ready())
  153. }
  154. }
  155. func TestRawNodeRestart(t *testing.T) {
  156. entries := []raftpb.Entry{
  157. {Term: 1, Index: 1},
  158. {Term: 1, Index: 2, Data: []byte("foo")},
  159. }
  160. st := raftpb.HardState{Term: 1, Commit: 1}
  161. want := Ready{
  162. HardState: emptyState,
  163. // commit up to commit index in st
  164. CommittedEntries: entries[:st.Commit],
  165. }
  166. storage := NewMemoryStorage()
  167. storage.SetHardState(st)
  168. storage.Append(entries)
  169. rawNode, err := NewRawNode(newTestConfig(1, nil, 10, 1, storage), nil)
  170. if err != nil {
  171. t.Fatal(err)
  172. }
  173. rd := rawNode.Ready()
  174. if !reflect.DeepEqual(rd, want) {
  175. t.Errorf("g = %+v,\n w %+v", rd, want)
  176. }
  177. rawNode.Advance(rd)
  178. if rawNode.HasReady() {
  179. t.Errorf("unexpected Ready: %+v", rawNode.Ready())
  180. }
  181. }
  182. func TestRawNodeRestartFromSnapshot(t *testing.T) {
  183. snap := raftpb.Snapshot{
  184. Metadata: raftpb.SnapshotMetadata{
  185. ConfState: raftpb.ConfState{Nodes: []uint64{1, 2}},
  186. Index: 2,
  187. Term: 1,
  188. },
  189. }
  190. entries := []raftpb.Entry{
  191. {Term: 1, Index: 3, Data: []byte("foo")},
  192. }
  193. st := raftpb.HardState{Term: 1, Commit: 3}
  194. want := Ready{
  195. HardState: emptyState,
  196. // commit up to commit index in st
  197. CommittedEntries: entries,
  198. }
  199. s := NewMemoryStorage()
  200. s.SetHardState(st)
  201. s.ApplySnapshot(snap)
  202. s.Append(entries)
  203. rawNode, err := NewRawNode(newTestConfig(1, nil, 10, 1, s), nil)
  204. if err != nil {
  205. t.Fatal(err)
  206. }
  207. if rd := rawNode.Ready(); !reflect.DeepEqual(rd, want) {
  208. t.Errorf("g = %+v,\n w %+v", rd, want)
  209. } else {
  210. rawNode.Advance(rd)
  211. }
  212. if rawNode.HasReady() {
  213. t.Errorf("unexpected Ready: %+v", rawNode.HasReady())
  214. }
  215. }
  216. // TestNodeAdvance from node_test.go has no equivalent in rawNode because there is
  217. // no dependency check between Ready() and Advance()
  218. func TestRawNodeStatus(t *testing.T) {
  219. storage := NewMemoryStorage()
  220. rawNode, err := NewRawNode(newTestConfig(1, nil, 10, 1, storage), []Peer{{ID: 1}})
  221. if err != nil {
  222. t.Fatal(err)
  223. }
  224. status := rawNode.Status()
  225. if status == nil {
  226. t.Errorf("expected status struct, got nil")
  227. }
  228. }