github.com/aergoio/aergo@v1.3.1/p2p/syncmanager_test.go (about) 1 /* 2 * @file 3 * @copyright defined in aergo/LICENSE.txt 4 */ 5 6 package p2p 7 8 import ( 9 "bytes" 10 "testing" 11 12 "github.com/aergoio/aergo-lib/log" 13 "github.com/aergoio/aergo/chain" 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 "github.com/stretchr/testify/assert" 20 "github.com/stretchr/testify/mock" 21 ) 22 23 func TestSyncManager_HandleBlockProducedNotice(t *testing.T) { 24 // only interested in max block size 25 chain.Init(1024*1024,"",false,0,0) 26 27 ctrl := gomock.NewController(t) 28 defer ctrl.Finish() 29 30 logger := log.NewLogger("test.p2p") 31 sampleBlock := &types.Block{Hash: dummyBlockHash} 32 txs := make([]*types.Tx,1) 33 txs[0] = &types.Tx{Hash:make([]byte,1024*1024*2)} 34 sampleBigBlock := &types.Block{Hash:dummyBlockHash,Body:&types.BlockBody{Txs:txs}} 35 var blkHash = types.ToBlockID(dummyBlockHash) 36 // test if new block notice comes 37 tests := []struct { 38 name string 39 put *types.BlockID 40 addedBlock *types.Block 41 42 wantActorCall bool 43 }{ 44 // 1. Succ : valid block hash and not exist in local 45 {"TSucc", nil, sampleBlock,true}, 46 // 2. Rare case - valid block hash but already exist in local cache 47 {"TExist", &blkHash, sampleBlock, false}, 48 {"TTooBigBlock", nil, sampleBigBlock,false}, 49 } 50 for _, test := range tests { 51 t.Run(test.name, func(t *testing.T) { 52 mockPM := p2pmock.NewMockPeerManager(ctrl) 53 mockActor := p2pmock.NewMockActorService(ctrl) 54 mockPeer := p2pmock.NewMockRemotePeer(ctrl) 55 mockPeer.EXPECT().Name().Return("16..aadecf@1").AnyTimes() 56 mockPeer.EXPECT().ID().Return(sampleMeta.ID).AnyTimes() 57 actorCallCnt := 0 58 if test.wantActorCall { 59 actorCallCnt = 1 60 } 61 mockActor.EXPECT().SendRequest(message.ChainSvc, gomock.Any()).Times(actorCallCnt) 62 63 target := newSyncManager(mockActor, mockPM, logger).(*syncManager) 64 if test.put != nil { 65 target.blkCache.Add(*test.put, true) 66 } 67 target.HandleBlockProducedNotice(mockPeer, test.addedBlock) 68 }) 69 } 70 } 71 72 func TestSyncManager_HandleNewBlockNotice(t *testing.T) { 73 // only interested in max block size 74 chain.Init(1024*1024,"",false,0,0) 75 76 ctrl := gomock.NewController(t) 77 defer ctrl.Finish() 78 79 logger := log.NewLogger("test.p2p") 80 sampleBlock := &types.Block{Hash: dummyBlockHash} 81 var blkHash types.BlockID 82 // test if new block notice comes 83 tests := []struct { 84 name string 85 put *types.BlockID 86 syncing bool 87 setup func(tt *testing.T, actor *p2pmock.MockActorService, ca *p2pmock.MockChainAccessor, peer *p2pmock.MockRemotePeer) (types.BlockID, *types.NewBlockNotice) 88 //verify func(tt *testing.T, actor *p2pmock.MockActorService, ca *p2pmock.MockChainAccessor) 89 }{ 90 // 1. Succ : valid block hash and not exist in local 91 {"TSucc", nil, false, 92 func(tt *testing.T, actor *p2pmock.MockActorService, ca *p2pmock.MockChainAccessor, peer *p2pmock.MockRemotePeer) (types.BlockID, *types.NewBlockNotice) { 93 ca.EXPECT().GetBlock(gomock.Any()).Return(nil, nil) 94 actor.EXPECT().GetChainAccessor().Return(ca) 95 copy(blkHash[:], dummyBlockHash) 96 actor.EXPECT().SendRequest(message.P2PSvc, gomock.Any()) 97 peer.EXPECT().Name().Return("16..aadecf@1") 98 return blkHash, &types.NewBlockNotice{BlockHash: dummyBlockHash} 99 }}, 100 // 1-1. Succ : valid block hash and exist in chainsvc, but not in cache 101 {"TSuccExistChain", nil, false, 102 func(tt *testing.T, actor *p2pmock.MockActorService, ca *p2pmock.MockChainAccessor, peer *p2pmock.MockRemotePeer) (types.BlockID, *types.NewBlockNotice) { 103 ca.EXPECT().GetBlock(gomock.Any()).Return(sampleBlock, nil) 104 copy(blkHash[:], dummyBlockHash) 105 actor.EXPECT().GetChainAccessor().Return(ca) 106 actor.EXPECT().SendRequest(message.P2PSvc, gomock.Any()).MaxTimes(0) 107 return blkHash, &types.NewBlockNotice{BlockHash: dummyBlockHash} 108 }}, 109 // 2. SuccCachehit : valid block hash but already exist in local cache 110 {"TSuccExistCache", &blkHash, false, 111 func(tt *testing.T, actor *p2pmock.MockActorService, ca *p2pmock.MockChainAccessor, peer *p2pmock.MockRemotePeer) (types.BlockID, *types.NewBlockNotice) { 112 ca.EXPECT().GetBlock(gomock.Any()).Return(sampleBlock, nil).MaxTimes(0) 113 copy(blkHash[:], dummyBlockHash) 114 115 //ca.AssertNotCalled(tt, "GetBlock", mock.AnythingOfType("[]uint8")) 116 //actor.EXPECT().AssertNotCalled(tt, "SendRequest", message.P2PSvc, mock.Anything) 117 actor.EXPECT().SendRequest(message.P2PSvc, mock.Anything).MaxTimes(0) 118 return blkHash, &types.NewBlockNotice{BlockHash: dummyBlockHash} 119 }}, 120 // 2. Busy : other sync worker is working 121 {"TBusy", &blkHash, true, 122 func(tt *testing.T, actor *p2pmock.MockActorService, ca *p2pmock.MockChainAccessor, peer *p2pmock.MockRemotePeer) (types.BlockID, *types.NewBlockNotice) { 123 copy(blkHash[:], dummyBlockHash) 124 actor.EXPECT().GetChainAccessor().MaxTimes(0) 125 actor.EXPECT().SendRequest(message.P2PSvc, gomock.Any()).MaxTimes(0) 126 return blkHash, &types.NewBlockNotice{BlockHash: dummyBlockHash} 127 }}, 128 } 129 for _, test := range tests { 130 t.Run(test.name, func(t *testing.T) { 131 mockPM := p2pmock.NewMockPeerManager(ctrl) 132 mockActor := p2pmock.NewMockActorService(ctrl) 133 mockCA := p2pmock.NewMockChainAccessor(ctrl) 134 mockPeer := p2pmock.NewMockRemotePeer(ctrl) 135 mockPeer.EXPECT().ID().Return(sampleMeta.ID) 136 137 _, data := test.setup(t, mockActor, mockCA, mockPeer) 138 target := newSyncManager(mockActor, mockPM, logger).(*syncManager) 139 if test.put != nil { 140 target.blkCache.Add(*test.put, true) 141 } 142 target.HandleNewBlockNotice(mockPeer, data) 143 //test.verify(t, mockActor, mockCA) 144 }) 145 } 146 } 147 148 func TestSyncManager_HandleNewTxNotice(t *testing.T) { 149 ctrl := gomock.NewController(t) 150 defer ctrl.Finish() 151 152 logger := log.NewLogger("test.p2p") 153 rawHashes := sampleTxs 154 txHashes := sampleTxHashes 155 156 // test if new block notice comes 157 tests := []struct { 158 name string 159 inCache []types.TxID 160 setup func(tt *testing.T, actor *p2pmock.MockActorService) 161 expected []types.TxID 162 }{ 163 // 1. Succ : valid tx hashes and not exist in local cache 164 {"TSuccAllNew", nil, 165 func(tt *testing.T, actor *p2pmock.MockActorService) { 166 actor.EXPECT().SendRequest(message.P2PSvc, gomock.Any()).DoAndReturn(func(name string, arg *message.GetTransactions) { 167 for i, hash := range arg.Hashes { 168 assert.True(tt, bytes.Equal(hash, txHashes[i][:])) 169 } 170 assert.True(tt, len(arg.Hashes) == len(txHashes)) 171 }) 172 }, sampleTxHashes}, 173 // 2. Succ : valid tx hashes and partially exist in local cache 174 {"TSuccExistPart", txHashes[2:], 175 func(tt *testing.T, actor *p2pmock.MockActorService) { 176 // only hashes not in cache are sent to method, which is first 2 hashes 177 actor.EXPECT().SendRequest(message.P2PSvc, gomock.Any()).DoAndReturn(func(name string, arg *message.GetTransactions) { 178 for i, hash := range arg.Hashes { 179 assert.True(tt, bytes.Equal(hash, txHashes[i][:])) 180 } 181 assert.True(tt, len(arg.Hashes) == 2) 182 }) 183 184 }, sampleTxHashes[:len(sampleTxHashes)-1]}, 185 // 3. Succ : valid tx hashes and all exist in local cache 186 {"TSuccExistAll", txHashes, 187 func(tt *testing.T, actor *p2pmock.MockActorService) { 188 actor.EXPECT().SendRequest(message.P2PSvc, gomock.Any()).MaxTimes(0) 189 }, sampleTxHashes[:0]}, 190 } 191 for _, test := range tests { 192 t.Run(test.name, func(t *testing.T) { 193 mockPM := p2pmock.NewMockPeerManager(ctrl) 194 mockActor := p2pmock.NewMockActorService(ctrl) 195 mockPeer := p2pmock.NewMockRemotePeer(ctrl) 196 mockPeer.EXPECT().ID().Return(sampleMeta.ID) 197 198 data := &types.NewTransactionsNotice{TxHashes: rawHashes} 199 200 test.setup(t, mockActor) 201 target := newSyncManager(mockActor, mockPM, logger) 202 if test.inCache != nil { 203 for _, hash := range test.inCache { 204 target.(*syncManager).txCache.Add(hash, true) 205 } 206 } 207 target.HandleNewTxNotice(mockPeer, txHashes, data) 208 }) 209 } 210 } 211 212 func TestSyncManager_HandleGetBlockResponse(t *testing.T) { 213 // only interested in max block size 214 chain.Init(1024*1024,"",false,0,0) 215 216 ctrl := gomock.NewController(t) 217 defer ctrl.Finish() 218 219 totalBlkCnt := len(sampleTxs) 220 sampleBlocks := make([]*types.Block, totalBlkCnt) 221 for i, hash := range sampleTxs { 222 sampleBlocks[i] = &types.Block{Hash: hash} 223 } 224 tests := []struct { 225 name string 226 respBlocks []*types.Block 227 228 // call count directly to chainservice 229 chainCallCnt int 230 }{ 231 // 1. message triggered by NewBlockNotice (maybe) 232 {"TSingleBlock", sampleBlocks[:1], 1}, 233 // 2. message triggered by newsyncer but not handled by it (caused by sync fail or timeout) 234 {"TZeroBlock", sampleBlocks[:0], 0}, 235 {"TMultiBlocks", sampleBlocks, 0}, 236 } 237 for _, test := range tests { 238 t.Run(test.name, func(t *testing.T) { 239 mockPM := p2pmock.NewMockPeerManager(ctrl) 240 mockActor := p2pmock.NewMockActorService(ctrl) 241 mockPeer := p2pmock.NewMockRemotePeer(ctrl) 242 mockPeer.EXPECT().ID().Return(sampleMeta.ID) 243 244 mockActor.EXPECT().SendRequest(gomock.Any(), gomock.Any()).Times(test.chainCallCnt) 245 dummyMsgID := p2pcommon.NewMsgID() 246 target := newSyncManager(mockActor, mockPM, logger).(*syncManager) 247 248 msg := p2pcommon.NewSimpleRespMsgVal(p2pcommon.PingResponse, p2pcommon.NewMsgID(), dummyMsgID) 249 resp := &types.GetBlockResponse{Blocks: test.respBlocks} 250 target.HandleGetBlockResponse(mockPeer, msg, resp) 251 252 //mockActor.AssertNumberOfCalls(t, "SendRequest", test.chainCallCnt) 253 }) 254 } 255 }