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

     1  /*
     2   * @file
     3   * @copyright defined in aergo/LICENSE.txt
     4   */
     5  
     6  package raftsupport
     7  
     8  import (
     9  	"bytes"
    10  	"sync/atomic"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/aergoio/aergo/message"
    15  	"github.com/aergoio/aergo/p2p/p2pcommon"
    16  	"github.com/aergoio/aergo/p2p/p2pmock"
    17  	"github.com/aergoio/aergo/types"
    18  	"github.com/golang/mock/gomock"
    19  )
    20  
    21  func TestStartGet(t *testing.T) {
    22  	ctrl := gomock.NewController(t)
    23  	defer ctrl.Finish()
    24  
    25  	type args struct {
    26  		peerCnt int
    27  		timeout time.Duration
    28  	}
    29  	tests := []struct {
    30  		name string
    31  		args args
    32  
    33  		wantSentCnt int  // count of sent to remote peers
    34  		wantTimeout bool // whether receiver returns result or not (=timeout)
    35  		wantErrResp bool // result with error or not
    36  	}{
    37  		{"TTimeout", args{peerCnt: 1}, 1, true, false},
    38  		{"TNoPeers", args{peerCnt: 0}, 0, false, true},
    39  	}
    40  	for _, tt := range tests {
    41  		t.Run(tt.name, func(t *testing.T) {
    42  			mockActor := p2pmock.NewMockActorService(ctrl)
    43  			mockMF := p2pmock.NewMockMoFactory(ctrl)
    44  			mockMo := createDummyMo(ctrl)
    45  			mockMF.EXPECT().NewMsgBlockRequestOrder(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockMo).Times(tt.wantSentCnt)
    46  			peers := make([]p2pcommon.RemotePeer, 0, tt.args.peerCnt)
    47  			for i := 0; i < tt.args.peerCnt; i++ {
    48  				dummyPeerID, _ := types.IDB58Decode("16Uiu2HAmFqptXPfcdaCdwipB2fhHATgKGVFVPehDAPZsDKSU7jRm")
    49  				peers = append(peers, createDummyPeer(ctrl, dummyPeerID, types.RUNNING))
    50  			}
    51  			replyChan := make(chan *message.GetClusterRsp)
    52  			dummyReq := &message.GetCluster{ReplyC: replyChan}
    53  			target := NewClusterInfoReceiver(mockActor, mockMF, peers, time.Millisecond, dummyReq)
    54  			target.StartGet()
    55  
    56  			if !tt.wantTimeout {
    57  				timer := time.NewTimer(time.Second * 2)
    58  				select {
    59  				case resp := <-replyChan:
    60  					if (resp.Err != nil) != tt.wantErrResp {
    61  						t.Errorf("resp error %v, wantErr %v ", resp.Err, tt.wantErrResp)
    62  					}
    63  				case <-timer.C:
    64  					t.Errorf("timeout occurred, want no time")
    65  				}
    66  			} else {
    67  				timer := time.NewTimer(time.Millisecond * 100)
    68  				select {
    69  				case resp := <-replyChan:
    70  					t.Errorf("unexpected response (%d mems, err:%v), want timeout", len(resp.Members), resp.Err)
    71  				case <-timer.C:
    72  					// expected timeout
    73  				}
    74  			}
    75  		})
    76  	}
    77  }
    78  
    79  func createDummyPeer(ctrl *gomock.Controller, pid types.PeerID, state types.PeerState) *p2pmock.MockRemotePeer {
    80  	mockPeer := p2pmock.NewMockRemotePeer(ctrl)
    81  	mockPeer.EXPECT().State().Return(state).AnyTimes()
    82  	mockPeer.EXPECT().ID().Return(pid).AnyTimes()
    83  	mockPeer.EXPECT().ConsumeRequest(gomock.Any()).AnyTimes()
    84  	mockPeer.EXPECT().SendMessage(gomock.Any()).AnyTimes()
    85  	return mockPeer
    86  }
    87  
    88  func createDummyMo(ctrl *gomock.Controller) *p2pmock.MockMsgOrder {
    89  	dummyMo := p2pmock.NewMockMsgOrder(ctrl)
    90  	dummyMo.EXPECT().IsNeedSign().Return(true).AnyTimes()
    91  	dummyMo.EXPECT().IsRequest().Return(true).AnyTimes()
    92  	dummyMo.EXPECT().GetProtocolID().Return(p2pcommon.NewTxNotice).AnyTimes()
    93  	dummyMo.EXPECT().GetMsgID().Return(p2pcommon.NewMsgID()).AnyTimes()
    94  	return dummyMo
    95  }
    96  
    97  func TestClusterInfoReceiver_trySendNextPeer(t *testing.T) {
    98  	ctrl := gomock.NewController(t)
    99  	defer ctrl.Finish()
   100  
   101  	type args struct {
   102  		stats []int
   103  	}
   104  	tests := []struct {
   105  		name string
   106  		args args
   107  
   108  		wantSentCnt int
   109  	}{
   110  		{"TAllRunning", args{[]int{1, 1, 1, 1, 1}}, 5},
   111  		{"TNoPeers", args{[]int{}}, 0},
   112  		{"TNoRunning", args{[]int{0, 0, 0, 0, 0}}, 0},
   113  		{"TMixed", args{[]int{0, 0, 1, 1, 1}}, 3},
   114  		{"TMixed2", args{[]int{1, 1, 0, 0, 0}}, 2},
   115  		{"TMixed3", args{[]int{1, 0, 1, 0, 0, 1}}, 3},
   116  		{"TMixed4", args{[]int{0, 1, 0, 1, 0, 1, 0}}, 3},
   117  	}
   118  	for _, tt := range tests {
   119  		t.Run(tt.name, func(t *testing.T) {
   120  			mockActor := p2pmock.NewMockActorService(ctrl)
   121  			mockMF := p2pmock.NewMockMoFactory(ctrl)
   122  			mockMo := createDummyMo(ctrl)
   123  			mockMF.EXPECT().NewMsgBlockRequestOrder(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockMo).Times(tt.wantSentCnt)
   124  			peers := make([]p2pcommon.RemotePeer, 0, len(tt.args.stats))
   125  			for _, run := range tt.args.stats {
   126  				dummyPeerID, _ := types.IDB58Decode("16Uiu2HAmFqptXPfcdaCdwipB2fhHATgKGVFVPehDAPZsDKSU7jRm")
   127  				stat := types.RUNNING
   128  				if run == 0 {
   129  					stat = types.STOPPING
   130  				}
   131  				peers = append(peers, createDummyPeer(ctrl, dummyPeerID, stat))
   132  			}
   133  
   134  			sentCnt := 0
   135  			replyChan := make(chan *message.GetClusterRsp)
   136  			dummyReq := &message.GetCluster{ReplyC: replyChan}
   137  			target := NewClusterInfoReceiver(mockActor, mockMF, peers, time.Millisecond, dummyReq)
   138  			for target.trySendNextPeer() {
   139  				sentCnt++
   140  			}
   141  
   142  			if sentCnt != tt.wantSentCnt {
   143  				t.Errorf("resp error %v, wantErr %v ", sentCnt, tt.wantSentCnt)
   144  			}
   145  		})
   146  	}
   147  }
   148  
   149  func TestClusterInfoReceiver_ReceiveResp(t *testing.T) {
   150  	sampleChainID := []byte("testChain")
   151  	members := make([]*types.MemberAttr,4)
   152  	ctrl := gomock.NewController(t)
   153  	defer ctrl.Finish()
   154  
   155  	type args struct {
   156  		stats []int
   157  	}
   158  	tests := []struct {
   159  		name string
   160  		args args
   161  
   162  		wantSentCnt int  // count of sent to remote peers
   163  		wantTimeout bool // whether receiver returns result or not (=timeout)
   164  		wantErrResp bool // result with error or not
   165  	}{
   166  		{"TAllRet", args{[]int{1, 1, 1, 1, 1}}, 1, false,false},
   167  		{"TErrRet", args{[]int{0, 0, 0, 0, 0}}, 5, false, true},
   168  		{"TMixed", args{[]int{0, 0, 1, 1, 1}}, 3, false, false},
   169  		{"TTimeout", args{[]int{0, 0}}, 3, true, true},
   170  	}
   171  	for _, tt := range tests {
   172  		t.Run(tt.name, func(t *testing.T) {
   173  			peers := make([]p2pcommon.RemotePeer, 0, len(tt.args.stats))
   174  
   175  			mockActor := p2pmock.NewMockActorService(ctrl)
   176  			mockMF := p2pmock.NewMockMoFactory(ctrl)
   177  			mockMo := createDummyMo(ctrl)
   178  			mockMF.EXPECT().NewMsgBlockRequestOrder(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockMo).Times(tt.wantSentCnt)
   179  
   180  			replyChan := make(chan *message.GetClusterRsp)
   181  			dummyReq := &message.GetCluster{ReplyC: replyChan}
   182  			target := NewClusterInfoReceiver(mockActor, mockMF, peers, time.Second, dummyReq)
   183  
   184  			seq := int32(0)
   185  			for i:=0; i<5; i++ {
   186  				dummyPeerID, _ := types.IDB58Decode("16Uiu2HAmFqptXPfcdaCdwipB2fhHATgKGVFVPehDAPZsDKSU7jRm")
   187  				stat := types.RUNNING
   188  				mockPeer := p2pmock.NewMockRemotePeer(ctrl)
   189  				mockPeer.EXPECT().State().Return(stat).AnyTimes()
   190  				mockPeer.EXPECT().ID().Return(dummyPeerID).AnyTimes()
   191  				mockPeer.EXPECT().ConsumeRequest(gomock.Any()).AnyTimes()
   192  				mockPeer.EXPECT().SendMessage(gomock.Any()).Do(func(mo p2pcommon.MsgOrder) {
   193  					time.Sleep(time.Millisecond*5)
   194  					callSeq := atomic.LoadInt32(&seq)
   195  					msg := p2pmock.NewMockMessage(ctrl)
   196  					msg.EXPECT().ID().Return(p2pcommon.NewMsgID()).AnyTimes()
   197  					msg.EXPECT().OriginalID().Return(p2pcommon.NewMsgID()).AnyTimes()
   198  					msg.EXPECT().Timestamp().Return(time.Now().UnixNano()).AnyTimes()
   199  					msg.EXPECT().Subprotocol().Return(p2pcommon.GetClusterResponse).AnyTimes()
   200  					if callSeq < int32(len(tt.args.stats)) {
   201  						err := ""
   202  						if tt.args.stats[callSeq] == 0 {
   203  							err = "getCluster failed"
   204  						}
   205  						body := &types.GetClusterInfoResponse{ChainID:sampleChainID, MbrAttrs:members, Error:err}
   206  						atomic.AddInt32(&seq, 1)
   207  						go target.ReceiveResp(msg, body)
   208  					} else {
   209  						atomic.AddInt32(&seq, 1)
   210  					}
   211  				}).MaxTimes(1)
   212  				peers = append(peers, mockPeer)
   213  			}
   214  			// force inject peers
   215  			target.peers = peers
   216  
   217  			target.StartGet()
   218  
   219  			if !tt.wantTimeout {
   220  				timer := time.NewTimer(time.Second * 2)
   221  				select {
   222  				case resp := <-replyChan:
   223  					if (resp.Err != nil) != tt.wantErrResp {
   224  						t.Errorf("resp error %v, wantErr %v ", resp.Err, tt.wantErrResp)
   225  					}
   226  					// receiver return valid result
   227  					if !tt.wantErrResp {
   228  						if !bytes.Equal(resp.ChainID, sampleChainID) {
   229  							t.Errorf("resp chainid %v, want %v ",resp.ChainID, sampleChainID)
   230  						}
   231  						if len(resp.Members) != len(members) {
   232  							t.Errorf("resp members %v, want %v ",resp.Members, len(members))
   233  						}
   234  					}
   235  				case <-timer.C:
   236  					t.Errorf("timeout occurred, want no time")
   237  				}
   238  			} else {
   239  				timer := time.NewTimer(time.Millisecond * 100)
   240  				select {
   241  				case resp := <-replyChan:
   242  					t.Errorf("unexpected response (%d mems, err:%v), want timeout", len(resp.Members), resp.Err)
   243  				case <-timer.C:
   244  					// expected timeout
   245  				}
   246  			}
   247  
   248  		})
   249  	}
   250  }