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

     1  /*
     2   * @file
     3   * @copyright defined in aergo/LICENSE.txt
     4   */
     5  
     6  package raftsupport
     7  
     8  import (
     9  	"bytes"
    10  	"github.com/aergoio/aergo-lib/log"
    11  	"github.com/aergoio/aergo/p2p/p2putil"
    12  	"sync"
    13  	"sync/atomic"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/aergoio/aergo/message"
    18  	"github.com/aergoio/aergo/p2p/p2pcommon"
    19  	"github.com/aergoio/aergo/p2p/p2pmock"
    20  	"github.com/aergoio/aergo/types"
    21  	"github.com/golang/mock/gomock"
    22  )
    23  
    24  func TestConcurrentClusterInfoReceiver_StartGet(t *testing.T) {
    25  	logger := log.NewLogger("raft.support.test")
    26  	ctrl := gomock.NewController(t)
    27  	defer ctrl.Finish()
    28  
    29  	type args struct {
    30  		peerCnt int
    31  		timeout time.Duration
    32  	}
    33  	tests := []struct {
    34  		name string
    35  		args args
    36  
    37  		wantSentCnt int  // count of sent to remote peers
    38  		wantTimeout bool // whether receiver returns result or not (=timeout)
    39  		wantErrResp bool // result with error or not
    40  	}{
    41  		{"TTimeout", args{peerCnt: 3}, 3, true, true},
    42  		{"TNoPeers", args{peerCnt: 0}, 0, false, true},
    43  	}
    44  	for _, tt := range tests {
    45  		t.Run(tt.name, func(t *testing.T) {
    46  			mockActor := p2pmock.NewMockActorService(ctrl)
    47  			mockMF := p2pmock.NewMockMoFactory(ctrl)
    48  			mockMo := createDummyMo(ctrl)
    49  			mockMF.EXPECT().NewMsgBlockRequestOrder(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockMo).Times(tt.wantSentCnt)
    50  			peers := make([]p2pcommon.RemotePeer, 0, tt.args.peerCnt)
    51  			for i := 0; i < tt.args.peerCnt; i++ {
    52  				dummyPeerID, _ := types.IDB58Decode("16Uiu2HAmFqptXPfcdaCdwipB2fhHATgKGVFVPehDAPZsDKSU7jRm")
    53  				peers = append(peers, createDummyPeer(ctrl, dummyPeerID, types.RUNNING))
    54  			}
    55  			replyChan := make(chan *message.GetClusterRsp)
    56  			dummyReq := &message.GetCluster{ReplyC: replyChan}
    57  			target := NewConcClusterInfoReceiver(mockActor, mockMF, peers, time.Millisecond*20, dummyReq, logger)
    58  			target.StartGet()
    59  
    60  			if !tt.wantTimeout {
    61  				timer := time.NewTimer(time.Second * 2)
    62  				select {
    63  				case resp := <-replyChan:
    64  					if (resp.Err != nil) != tt.wantErrResp {
    65  						t.Errorf("resp error %v, wantErr %v ", resp.Err, tt.wantErrResp)
    66  					}
    67  				case <-timer.C:
    68  					t.Errorf("timeout occurred, want no time")
    69  				}
    70  			} else {
    71  				timer := time.NewTimer(time.Millisecond * 200)
    72  				select {
    73  				case resp := <-replyChan:
    74  					if (resp.Err != nil) != tt.wantErrResp {
    75  						t.Errorf("resp error %v, wantErr %v ", resp.Err, tt.wantErrResp)
    76  					}
    77  				case <-timer.C:
    78  					// expected timeout
    79  				}
    80  			}
    81  		})
    82  	}
    83  }
    84  
    85  func TestConcurrentClusterInfoReceiver_trySendAllPeers(t *testing.T) {
    86  	logger := log.NewLogger("raft.support.test")
    87  	ctrl := gomock.NewController(t)
    88  	defer ctrl.Finish()
    89  
    90  	type args struct {
    91  		stats []int
    92  	}
    93  	tests := []struct {
    94  		name string
    95  		args args
    96  
    97  		wantSentCnt int
    98  		wantSucc    bool
    99  	}{
   100  		{"TAllRunning", args{[]int{1, 1, 1, 1, 1}}, 5, true},
   101  		{"TNoPeers", args{[]int{}}, 0, false},
   102  		{"TNoRunning", args{[]int{0, 0, 0, 0, 0}}, 0, false},
   103  		{"TMixed", args{[]int{0, 0, 1, 1, 1}}, 3, true},
   104  		{"TMixed2", args{[]int{1, 1, 0, 0, 0}}, 2, false},
   105  		{"TMixed3", args{[]int{1, 0, 1, 0, 1, 1}}, 4, true},
   106  		{"TMixed3Fail", args{[]int{1, 0, 1, 0, 0, 1}}, 3, false},
   107  		{"TMixed4", args{[]int{0, 1, 0, 1, 0, 1, 0}}, 3, false},
   108  		{"TSingle", args{[]int{1}}, 1, true},
   109  		{"TSingleFail", args{[]int{0}}, 0, false},
   110  		{"TTwoFail", args{[]int{0, 1}}, 1, false},
   111  		{"TTwoFail2", args{[]int{0, 0}}, 0, false},
   112  		{"TThree", args{[]int{1, 0, 1}}, 2, true},
   113  		{"TThreeFail", args{[]int{1, 0, 0}}, 1, false},
   114  	}
   115  	for _, tt := range tests {
   116  		t.Run(tt.name, func(t *testing.T) {
   117  			mockActor := p2pmock.NewMockActorService(ctrl)
   118  			mockMF := p2pmock.NewMockMoFactory(ctrl)
   119  			mockMF.EXPECT().NewMsgBlockRequestOrder(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(respReceiver p2pcommon.ResponseReceiver, protocolID p2pcommon.SubProtocol, message p2pcommon.MessageBody) p2pcommon.MsgOrder {
   120  				return createDummyMo(ctrl)
   121  			}).Times(tt.wantSentCnt)
   122  			peers := make([]p2pcommon.RemotePeer, 0, len(tt.args.stats))
   123  			for _, run := range tt.args.stats {
   124  				dummyPeerID := p2putil.RandomPeerID()
   125  				stat := types.RUNNING
   126  				if run == 0 {
   127  					stat = types.STOPPING
   128  				}
   129  				peers = append(peers, createDummyPeer(ctrl, dummyPeerID, stat))
   130  			}
   131  
   132  			replyChan := make(chan *message.GetClusterRsp)
   133  			dummyReq := &message.GetCluster{ReplyC: replyChan}
   134  			target := NewConcClusterInfoReceiver(mockActor, mockMF, peers, time.Millisecond, dummyReq, logger)
   135  			ret := target.trySendAllPeers()
   136  
   137  			if len(target.sent) != tt.wantSentCnt {
   138  				t.Errorf("trySendAllPeers sentCount %v, want %v ", len(target.sent), tt.wantSentCnt)
   139  			}
   140  			if ret != tt.wantSucc {
   141  				t.Errorf("trySendAllPeers() ret %v, want %v ", ret, tt.wantSucc)
   142  			}
   143  		})
   144  	}
   145  }
   146  
   147  type retStat int
   148  
   149  const (
   150  	ERR = -1
   151  	NOR = -2
   152  )
   153  
   154  func TestConcurrentClusterInfoReceiver_ReceiveResp(t *testing.T) {
   155  	logger := log.NewLogger("raft.support.test")
   156  
   157  	sampleChainID := []byte("testChain")
   158  	members := make([]*types.MemberAttr, 4)
   159  
   160  	type args struct {
   161  		retStats []retStat
   162  	}
   163  	tests := []struct {
   164  		name string
   165  		args args
   166  
   167  		wantBestNo int  // count of sent to remote peers
   168  		wantErrResp bool // result with error or not
   169  	}{
   170  		{"TAllSame", args{[]retStat{10, 10, 10, 10, 10}}, 10, false},
   171  		{"TErrRet", args{ []retStat{ERR,ERR,ERR,ERR,ERR}}, 10,  true},
   172  		{"TMixed", args{ []retStat{100, ERR, 99, 98, ERR}}, 100,  false},
   173  		{"TMixed2", args{ []retStat{100, ERR, NOR, ERR, 100}}, 100,  true},
   174  		{"TTimeSucc", args{ []retStat{NOR, 99, NOR, 98, 99}}, 99,  false},
   175  		{"TTime1", args{ []retStat{NOR, ERR, NOR, 100, 100}}, 100,  true},
   176  		{"TTime2", args{ []retStat{NOR, NOR, NOR, 100, 100}}, 100,  true},
   177  	}
   178  	for _, tt := range tests {
   179  		t.Run(tt.name, func(t *testing.T) {
   180  			ctrl := gomock.NewController(t)
   181  			peers := make([]p2pcommon.RemotePeer, len(tt.args.retStats))
   182  
   183  			mockActor := p2pmock.NewMockActorService(ctrl)
   184  			mockMF := p2pmock.NewMockMoFactory(ctrl)
   185  			mockMO := p2pmock.NewMockMsgOrder(ctrl)
   186  
   187  			replyChan := make(chan *message.GetClusterRsp)
   188  			dummyReq := &message.GetCluster{ReplyC: replyChan}
   189  
   190  			sMap := make(map[p2pcommon.MsgID]p2pcommon.RemotePeer)
   191  			rHeads := make([]p2pcommon.Message, 0, len(tt.args.retStats))
   192  			rBodies := make([]*types.GetClusterInfoResponse, 0, len(tt.args.retStats))
   193  
   194  			mockMF.EXPECT().NewMsgBlockRequestOrder(gomock.Any(), p2pcommon.GetClusterRequest, gomock.Any()).
   195  				Times(len(tt.args.retStats)).Return(mockMO)
   196  			sentTrigger := int32(0)
   197  			prevCall := (*gomock.Call)(nil)
   198  			for i, st := range tt.args.retStats {
   199  				dummyPeerID := p2putil.RandomPeerID()
   200  				msgID := p2pcommon.NewMsgID()
   201  				stat := types.RUNNING
   202  				mockPeer := p2pmock.NewMockRemotePeer(ctrl)
   203  				mockPeer.EXPECT().State().Return(stat).AnyTimes()
   204  				mockPeer.EXPECT().ID().Return(dummyPeerID).AnyTimes()
   205  				mockPeer.EXPECT().Name().Return("peer" + p2putil.ShortForm(dummyPeerID)).AnyTimes()
   206  				mockPeer.EXPECT().ConsumeRequest(gomock.Any()).AnyTimes()
   207  				mockPeer.EXPECT().SendMessage(mockMO).Do(func(arg interface{}) {
   208  					atomic.StoreInt32(&sentTrigger, 1)})
   209  				peers[i] = mockPeer
   210  				sMap[msgID] = mockPeer
   211  
   212  				// mockMO
   213  				if prevCall == nil {
   214  					prevCall = mockMO.EXPECT().GetMsgID().Return(msgID)
   215  				} else {
   216  					prevCall = mockMO.EXPECT().GetMsgID().Return(msgID).After(prevCall)
   217  				}
   218  
   219  				head := p2pcommon.NewLiteMessageValue(p2pcommon.RaftWrapperMessage, p2pcommon.NewMsgID(), msgID, time.Now().UnixNano())
   220  				body := &types.GetClusterInfoResponse{ChainID: sampleChainID, MbrAttrs: members, Error: ""}
   221  				switch st {
   222  				case NOR:
   223  					continue
   224  				case ERR:
   225  					body.Error = "getCluster failed"
   226  					fallthrough
   227  				default:
   228  					rHeads = append(rHeads, head)
   229  					rBodies = append(rBodies, body)
   230  				}
   231  			}
   232  			ttl := time.Second>>4
   233  			target := NewConcClusterInfoReceiver(mockActor, mockMF, peers, ttl , dummyReq, logger)
   234  			target.StartGet()
   235  
   236  			wg := sync.WaitGroup{}
   237  			wg.Add(len(rHeads))
   238  			bar := sync.WaitGroup{}
   239  			bar.Add(1)
   240  			for i, head := range rHeads {
   241  				go func(msg p2pcommon.Message, body *types.GetClusterInfoResponse) {
   242  					wg.Done()
   243  					bar.Wait()
   244  					target.ReceiveResp(msg, body)
   245  				}(head, rBodies[i])
   246  			}
   247  			for atomic.LoadInt32(&sentTrigger) == 0 {
   248  				time.Sleep(time.Millisecond)
   249  			}
   250  			wg.Wait()
   251  			bar.Done()
   252  			timer := time.NewTimer(ttl << 1)
   253  			select {
   254  			case resp := <-replyChan:
   255  				if (resp.Err != nil) != tt.wantErrResp {
   256  					t.Errorf("resp error %v, wantErr %v ", resp.Err, tt.wantErrResp)
   257  				}
   258  				// receiver return valid result
   259  				if !tt.wantErrResp {
   260  					if !bytes.Equal(resp.ChainID, sampleChainID) {
   261  						t.Errorf("resp chainid %v, want %v ", resp.ChainID, sampleChainID)
   262  					}
   263  					if len(resp.Members) != len(members) {
   264  						t.Errorf("resp members %v, want %v ", len(resp.Members), len(members))
   265  					}
   266  				}
   267  			case <-timer.C:
   268  				t.Fatalf("timeout occurred, want not")
   269  			}
   270  
   271  			ctrl.Finish()
   272  		})
   273  	}
   274  }