github.com/aergoio/aergo@v1.3.1/p2p/raftsupport/rafttransport_test.go (about)

     1  /*
     2   * @file
     3   * @copyright defined in aergo/LICENSE.txt
     4   */
     5  
     6  package raftsupport
     7  
     8  import (
     9  	"io"
    10  	"reflect"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/aergoio/aergo-lib/log"
    15  	"github.com/aergoio/aergo/consensus"
    16  	"github.com/aergoio/aergo/consensus/impl/raftv2"
    17  	"github.com/aergoio/aergo/p2p/p2pcommon"
    18  	"github.com/aergoio/aergo/p2p/p2pmock"
    19  	"github.com/aergoio/aergo/p2p/p2putil"
    20  	"github.com/aergoio/aergo/types"
    21  	"github.com/aergoio/etcd/raft"
    22  	"github.com/aergoio/etcd/raft/raftpb"
    23  	"github.com/aergoio/etcd/snap"
    24  	"github.com/golang/mock/gomock"
    25  	"github.com/pkg/errors"
    26  )
    27  
    28  func TestAergoRaftTransport_SendSnapshot(t *testing.T) {
    29  	// SendSnapshot acquire snap.Message, which must be closed after using.
    30  
    31  	logger := log.NewLogger("raft.support.test")
    32  	dummyChainID := make([]byte, 32)
    33  	dummyPeerID := p2putil.RandomPeerID()
    34  	dummyPeerMeta := p2pcommon.PeerMeta{ID: dummyPeerID}
    35  	dummyMemID := uint64(112345531252)
    36  	dummyMember := &consensus.Member{types.MemberAttr{ID: dummyMemID, PeerID: []byte(dummyPeerID)}}
    37  
    38  	type mockOPs struct {
    39  		toID      uint64
    40  		raMem     *consensus.Member
    41  		pmGetPeer bool
    42  		sr        error // snapshotSender result
    43  	}
    44  	tests := []struct {
    45  		name string
    46  
    47  		mOP mockOPs
    48  
    49  		wantResult bool
    50  	}{
    51  		{"TSucc", mockOPs{toID: dummyMemID, raMem: dummyMember, pmGetPeer: true}, true},
    52  		{"TWrongID", mockOPs{toID: 0, raMem: dummyMember, pmGetPeer: true}, false},
    53  		{"TInvalidMemberID", mockOPs{toID: dummyMemID, raMem: nil, pmGetPeer: true}, false},
    54  		{"TNotConnPeer", mockOPs{toID: dummyMemID, raMem: dummyMember, pmGetPeer: false}, false},
    55  		{"TTransportErr", mockOPs{toID: dummyMemID, raMem: dummyMember, pmGetPeer: true, sr: errors.New("transport error")}, false},
    56  	}
    57  	for _, tt := range tests {
    58  		t.Run(tt.name, func(t *testing.T) {
    59  			ctrl := gomock.NewController(t)
    60  			defer ctrl.Finish()
    61  
    62  			mockNT := p2pmock.NewMockNetworkTransport(ctrl)
    63  			mockPM := p2pmock.NewMockPeerManager(ctrl)
    64  			mockMF := p2pmock.NewMockMoFactory(ctrl)
    65  			mockCA := p2pmock.NewMockConsensusAccessor(ctrl)
    66  			mockRA := p2pmock.NewMockAergoRaftAccessor(ctrl)
    67  			mockRC := p2pmock.NewMockReadCloser(ctrl)
    68  			mockPeer := p2pmock.NewMockRemotePeer(ctrl)
    69  			dummyCl := raftv2.NewCluster(dummyChainID, nil, "test", dummyPeerID, 0, nil)
    70  
    71  			// not checked mock operations
    72  			mockCA.EXPECT().RaftAccessor().Return(mockRA).AnyTimes()
    73  			mockPM.EXPECT().AddPeerEventListener(gomock.Any()).AnyTimes()
    74  			mockPeer.EXPECT().ID().Return(dummyPeerID).AnyTimes()
    75  			mockPeer.EXPECT().Name().Return(dummyPeerID.ShortString()).AnyTimes()
    76  			mockPeer.EXPECT().Meta().Return(dummyPeerMeta).AnyTimes()
    77  			mockRC.EXPECT().Read(gomock.Any()).DoAndReturn(func(buf []byte) (int, error) {
    78  				return len(buf), nil
    79  			}).AnyTimes()
    80  
    81  			// checked mock operations
    82  			// close must be called in any cases
    83  			mockRC.EXPECT().Close().Times(1)
    84  			if tt.mOP.toID != 0 {
    85  				mockRA.EXPECT().GetMemberByID(tt.mOP.toID).Return(tt.mOP.raMem).Times(1)
    86  				if tt.mOP.raMem != nil {
    87  					if tt.mOP.pmGetPeer {
    88  						mockPM.EXPECT().GetPeer(dummyPeerID).Return(mockPeer, true).Times(1)
    89  					} else {
    90  						mockPM.EXPECT().GetPeer(dummyPeerID).Return(nil, false).Times(1)
    91  						mockRA.EXPECT().ReportUnreachable(dummyPeerID)
    92  						mockRA.EXPECT().ReportSnapshot(dummyPeerID, raft.SnapshotFailure)
    93  					}
    94  				}
    95  			}
    96  			// emulate snapshotSender
    97  			rs := raftpb.Message{To: tt.mOP.toID}
    98  			msg := snap.NewMessage(rs, mockRC, 1000)
    99  			ssf := &snapStubFactory{serr: tt.mOP.sr, rsize: rs.Size() + 1000}
   100  
   101  			target := NewAergoRaftTransport(logger, mockNT, mockPM, mockMF, mockCA, dummyCl)
   102  			target.snapF = ssf
   103  
   104  			target.SendSnapshot(*msg)
   105  
   106  			timer := time.NewTimer(time.Millisecond * 100)
   107  			select {
   108  			case r := <-msg.CloseNotify():
   109  				if r != tt.wantResult {
   110  					t.Errorf("close result %v , want %v", r, tt.wantResult)
   111  				}
   112  			case <-timer.C:
   113  				t.Error("unexpected timeout")
   114  			}
   115  		})
   116  	}
   117  }
   118  
   119  func TestAergoRaftTransport_NewSnapshotSender(t *testing.T) {
   120  	logger := log.NewLogger("raft.support.test")
   121  	dummyChainID := make([]byte, 32)
   122  	dummyPeerID := p2putil.RandomPeerID()
   123  
   124  	ctrl := gomock.NewController(t)
   125  	defer ctrl.Finish()
   126  
   127  	mockNT := p2pmock.NewMockNetworkTransport(ctrl)
   128  	mockPM := p2pmock.NewMockPeerManager(ctrl)
   129  	mockMF := p2pmock.NewMockMoFactory(ctrl)
   130  	mockCA := p2pmock.NewMockConsensusAccessor(ctrl)
   131  	mockRA := p2pmock.NewMockAergoRaftAccessor(ctrl)
   132  	mockRWC := p2pmock.NewMockReadWriteCloser(ctrl)
   133  	mockPeer := p2pmock.NewMockRemotePeer(ctrl)
   134  	dummyCl := raftv2.NewCluster(dummyChainID, nil, "test", dummyPeerID, 0, nil)
   135  	// not checked mock operations
   136  	mockCA.EXPECT().RaftAccessor().Return(mockRA).AnyTimes()
   137  	mockPM.EXPECT().AddPeerEventListener(gomock.Any()).AnyTimes()
   138  
   139  	target := NewAergoRaftTransport(logger, mockNT, mockPM, mockMF, mockCA, dummyCl)
   140  	got := target.NewSnapshotSender(mockPeer)
   141  	if _, ok := got.(*snapshotSender); !ok {
   142  		t.Errorf("AergoRaftTransport.NewSnapshotSender() type is differ: %v", reflect.TypeOf(got).Name())
   143  	} else {
   144  		if got.(*snapshotSender).peer != mockPeer {
   145  			t.Errorf("AergoRaftTransport.NewSnapshotSender() assign failed")
   146  		}
   147  	}
   148  
   149  	got2 := target.NewSnapshotReceiver(mockPeer, mockRWC)
   150  	if _, ok := got2.(*snapshotReceiver); !ok {
   151  		t.Errorf("AergoRaftTransport.NewSnapshotSender() type is differ: %v", reflect.TypeOf(got).Name())
   152  	} else {
   153  		if got2.(*snapshotReceiver).peer != mockPeer {
   154  			t.Errorf("AergoRaftTransport.NewSnapshotSender() assign failed")
   155  		}
   156  	}
   157  }
   158  
   159  type snapStubFactory struct {
   160  	rsize int
   161  	serr  error
   162  }
   163  
   164  func (f snapStubFactory) NewSnapshotSender(peer p2pcommon.RemotePeer) SnapshotSender {
   165  	return &snapSenderStub{f.rsize, f.serr}
   166  }
   167  
   168  func (snapStubFactory) NewSnapshotReceiver(peer p2pcommon.RemotePeer, rwc io.ReadWriteCloser) SnapshotReceiver {
   169  	return &snapReceiverStub{}
   170  }
   171  
   172  type snapSenderStub struct {
   173  	rsize int
   174  	err   error
   175  }
   176  
   177  func (s snapSenderStub) Send(snapMsg *snap.Message) {
   178  	if s.rsize > 0 {
   179  		buf := make([]byte, s.rsize)
   180  		snapMsg.ReadCloser.Read(buf)
   181  	}
   182  	snapMsg.CloseWithError(s.err)
   183  }
   184  
   185  type snapReceiverStub struct {
   186  }
   187  
   188  func (r snapReceiverStub) Receive() {
   189  }
   190  
   191  func TestAergoRaftTransport_Send(t *testing.T) {
   192  	// SendSnapshot acquire snap.Message, which must be closed after using.
   193  
   194  	logger := log.NewLogger("raft.support.test")
   195  	dummyChainID := make([]byte, 32)
   196  	dummyPeerID := p2putil.RandomPeerID()
   197  	dummyPeerMeta := p2pcommon.PeerMeta{ID: dummyPeerID}
   198  	dummyMemID := uint64(11111)
   199  	dummyMember := &consensus.Member{types.MemberAttr{ID: dummyMemID, PeerID: []byte(dummyPeerID)}}
   200  	unreachableMemID := uint64(33333)
   201  	unreachablePeerID := p2putil.RandomPeerID()
   202  	unreachableMember := &consensus.Member{types.MemberAttr{ID:unreachableMemID , PeerID: []byte(unreachablePeerID)}}
   203  
   204  	zeroM := raftpb.Message{To:0, Type:raftpb.MsgApp}
   205  	notM := raftpb.Message{To:98767, Type:raftpb.MsgApp}
   206  	memM := raftpb.Message{To:dummyMemID, Type:raftpb.MsgApp}
   207  	unM := raftpb.Message{To:unreachableMemID, Type:raftpb.MsgApp}
   208  	type args struct {
   209  		toID      uint64
   210  		raMem     *consensus.Member
   211  		pmGetPeer bool
   212  		sr        error // snapshotSender result
   213  	}
   214  	tests := []struct {
   215  		name string
   216  		msgs []raftpb.Message
   217  
   218  		wantChkMemCnt int
   219  		wantGetPeerCnt int
   220  		wantSendCnt int
   221  		wantUnreachCnt int
   222  		wantResult bool
   223  	}{
   224  		{"TSingle", ToM(memM), 1,1,1, 0, true},
   225  		{"TMulti", ToM(memM,memM,memM), 3,3,3, 0, true},
   226  		{"TWZero", ToM(memM,zeroM,zeroM,memM), 2,2, 2,0, true},
   227  		{"TInvalidM", ToM(notM,notM), 2,0,0, 0,true},
   228  		{"TUnreachable", ToM(unM, unM, memM), 3,3,1, 2,true},
   229  
   230  		//{"TWrongID", args{toID: 0, raMem: dummyMember, pmGetPeer: true}, false},
   231  		//{"TInvalidMemberID", args{toID: dummyMemID, raMem: nil, pmGetPeer: true}, false},
   232  		//{"TNotConnPeer", args{toID: dummyMemID, raMem: dummyMember, pmGetPeer: false}, false},
   233  		//{"TTransportErr", args{toID: dummyMemID, raMem: dummyMember, pmGetPeer: true, sr: errors.New("transport error")}, false},
   234  	}
   235  	for _, tt := range tests {
   236  		t.Run(tt.name, func(t *testing.T) {
   237  			ctrl := gomock.NewController(t)
   238  			defer ctrl.Finish()
   239  
   240  			mockNT := p2pmock.NewMockNetworkTransport(ctrl)
   241  			mockPM := p2pmock.NewMockPeerManager(ctrl)
   242  			mockMF := p2pmock.NewMockMoFactory(ctrl)
   243  			mockCA := p2pmock.NewMockConsensusAccessor(ctrl)
   244  			mockRA := p2pmock.NewMockAergoRaftAccessor(ctrl)
   245  			mockRC := p2pmock.NewMockReadCloser(ctrl)
   246  			mockPeer := p2pmock.NewMockRemotePeer(ctrl)
   247  			dummyCl := raftv2.NewCluster(dummyChainID, nil, "test", dummyPeerID, 0, nil)
   248  			dummyMO := p2pmock.NewMockMsgOrder(ctrl)
   249  
   250  			// not checked mock operations
   251  			mockCA.EXPECT().RaftAccessor().Return(mockRA).AnyTimes()
   252  			mockPM.EXPECT().AddPeerEventListener(gomock.Any()).AnyTimes()
   253  			mockPeer.EXPECT().ID().Return(dummyPeerID).AnyTimes()
   254  			mockPeer.EXPECT().Name().Return(dummyPeerID.ShortString()).AnyTimes()
   255  			mockPeer.EXPECT().Meta().Return(dummyPeerMeta).AnyTimes()
   256  			mockRC.EXPECT().Read(gomock.Any()).DoAndReturn(func(buf []byte) (int, error) {
   257  				return len(buf), nil
   258  			}).AnyTimes()
   259  
   260  			// checked mock operations
   261  			mockRA.EXPECT().GetMemberByID(gomock.Any()).DoAndReturn(func(id uint64) *consensus.Member {
   262  				if id == dummyMemID {
   263  					return dummyMember
   264  				} else if id == unreachableMemID {
   265  					return unreachableMember
   266  				} else {
   267  					return nil
   268  				}
   269  			}).Times(tt.wantChkMemCnt)
   270  			mockPM.EXPECT().GetPeer(gomock.Any()).DoAndReturn(func(pid types.PeerID) (p2pcommon.RemotePeer, bool) {
   271  				if pid == dummyPeerID {
   272  					return mockPeer, true
   273  				} else {
   274  					return nil, false
   275  				}
   276  			}).MaxTimes(tt.wantGetPeerCnt)
   277  			mockMF.EXPECT().NewRaftMsgOrder(gomock.Any(), gomock.Any()).Return(dummyMO).Times(tt.wantSendCnt)
   278  			mockPeer.EXPECT().SendMessage(gomock.Any()).Times(tt.wantSendCnt)
   279  			mockRA.EXPECT().ReportUnreachable(unreachablePeerID).Times(tt.wantUnreachCnt)
   280  
   281  
   282  			target := NewAergoRaftTransport(logger, mockNT, mockPM, mockMF, mockCA, dummyCl)
   283  
   284  			target.Send(tt.msgs)
   285  
   286  		})
   287  	}
   288  }
   289  
   290  func ToM(ms ...raftpb.Message) []raftpb.Message {
   291  	return ms
   292  }