github.com/koko1123/flow-go-1@v0.29.6/network/test/blob_service_test.go (about) 1 package test 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "testing" 8 "time" 9 10 "github.com/ipfs/go-cid" 11 "github.com/ipfs/go-datastore" 12 "github.com/ipfs/go-datastore/sync" 13 blockstore "github.com/ipfs/go-ipfs-blockstore" 14 "github.com/rs/zerolog" 15 "github.com/stretchr/testify/suite" 16 "go.uber.org/atomic" 17 18 "github.com/koko1123/flow-go-1/network/p2p/dht" 19 20 "github.com/koko1123/flow-go-1/utils/unittest" 21 22 "github.com/koko1123/flow-go-1/model/flow" 23 "github.com/koko1123/flow-go-1/module/blobs" 24 "github.com/koko1123/flow-go-1/module/irrecoverable" 25 "github.com/koko1123/flow-go-1/module/util" 26 "github.com/koko1123/flow-go-1/network" 27 "github.com/koko1123/flow-go-1/network/channels" 28 "github.com/koko1123/flow-go-1/network/internal/testutils" 29 "github.com/koko1123/flow-go-1/network/mocknetwork" 30 ) 31 32 // conditionalTopology is a topology that behaves like the underlying topology when the condition is true, 33 // otherwise returns an empty identity list. 34 type conditionalTopology struct { 35 top network.Topology 36 condition func() bool 37 } 38 39 var _ network.Topology = (*conditionalTopology)(nil) 40 41 func (t *conditionalTopology) Fanout(ids flow.IdentityList) flow.IdentityList { 42 if t.condition() { 43 return t.top.Fanout(ids) 44 } else { 45 return flow.IdentityList{} 46 } 47 } 48 49 type BlobServiceTestSuite struct { 50 suite.Suite 51 52 cancel context.CancelFunc 53 networks []network.Network 54 blobServices []network.BlobService 55 datastores []datastore.Batching 56 blobCids []cid.Cid 57 numNodes int 58 } 59 60 func TestBlobService(t *testing.T) { 61 t.Parallel() 62 suite.Run(t, new(BlobServiceTestSuite)) 63 } 64 65 func (suite *BlobServiceTestSuite) putBlob(ds datastore.Batching, blob blobs.Blob) { 66 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 67 defer cancel() 68 suite.Require().NoError(blockstore.NewBlockstore(ds).Put(ctx, blob)) 69 } 70 71 func (suite *BlobServiceTestSuite) SetupTest() { 72 suite.numNodes = 3 73 74 logger := zerolog.New(os.Stdout) 75 76 // Bitswap listens to connect events but doesn't iterate over existing connections, and fixing this without 77 // race conditions is tricky given the way the code is architected. As a result, libP2P hosts must first listen 78 // on Bitswap before connecting to each other, otherwise their Bitswap requests may never reach each other. 79 // See https://github.com/ipfs/go-bitswap/issues/525 for more details. 80 topologyActive := atomic.NewBool(false) 81 82 ctx, cancel := context.WithCancel(context.Background()) 83 suite.cancel = cancel 84 85 signalerCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) 86 87 ids, nodes, mws, networks, _ := testutils.GenerateIDsMiddlewaresNetworks( 88 suite.T(), 89 suite.numNodes, 90 logger, 91 unittest.NetworkCodec(), 92 mocknetwork.NewViolationsConsumer(suite.T()), 93 testutils.WithDHT("blob_service_test", dht.AsServer()), 94 testutils.WithPeerUpdateInterval(time.Second), 95 ) 96 suite.networks = networks 97 98 testutils.StartNodesAndNetworks(signalerCtx, suite.T(), nodes, networks, 100*time.Millisecond) 99 100 blobExchangeChannel := channels.Channel("blob-exchange") 101 102 for i, net := range networks { 103 ds := sync.MutexWrap(datastore.NewMapDatastore()) 104 suite.datastores = append(suite.datastores, ds) 105 blob := blobs.NewBlob([]byte(fmt.Sprintf("foo%v", i))) 106 suite.blobCids = append(suite.blobCids, blob.Cid()) 107 suite.putBlob(ds, blob) 108 blobService, err := net.RegisterBlobService(blobExchangeChannel, ds) 109 suite.Require().NoError(err) 110 <-blobService.Ready() 111 suite.blobServices = append(suite.blobServices, blobService) 112 } 113 114 // let nodes connect to each other only after they are all listening on Bitswap 115 topologyActive.Store(true) 116 suite.Require().Eventually(func() bool { 117 for i, mw := range mws { 118 for j := i + 1; j < suite.numNodes; j++ { 119 connected, err := mw.IsConnected(ids[j].NodeID) 120 suite.Require().NoError(err) 121 if !connected { 122 return false 123 } 124 } 125 } 126 return true 127 }, 3*time.Second, 100*time.Millisecond) 128 } 129 130 func (suite *BlobServiceTestSuite) TearDownTest() { 131 suite.cancel() 132 133 netDoneChans := make([]<-chan struct{}, len(suite.networks)) 134 for i, net := range suite.networks { 135 netDoneChans[i] = net.Done() 136 } 137 <-util.AllClosed(netDoneChans...) 138 139 suite.networks = nil 140 suite.cancel = nil 141 suite.blobServices = nil 142 suite.datastores = nil 143 suite.blobCids = nil 144 } 145 146 func (suite *BlobServiceTestSuite) TestGetBlobs() { 147 for i, bex := range suite.blobServices { 148 // check that we can get all other blobs 149 var blobsToGet []cid.Cid 150 unreceivedBlobs := make(map[cid.Cid]struct{}) 151 for j, blobCid := range suite.blobCids { 152 if j != i { 153 blobsToGet = append(blobsToGet, blobCid) 154 unreceivedBlobs[blobCid] = struct{}{} 155 } 156 } 157 158 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 159 defer cancel() 160 161 blobs := bex.GetBlobs(ctx, blobsToGet) 162 163 for blob := range blobs { 164 delete(unreceivedBlobs, blob.Cid()) 165 } 166 167 for c := range unreceivedBlobs { 168 suite.T().Errorf("Blob %v not received by node %v", c, i) 169 } 170 } 171 } 172 173 func (suite *BlobServiceTestSuite) TestGetBlobsWithSession() { 174 for i, bex := range suite.blobServices { 175 // check that we can get all other blobs in a single session 176 blobsToGet := make(map[cid.Cid]struct{}) 177 for j, blobCid := range suite.blobCids { 178 if j != i { 179 blobsToGet[blobCid] = struct{}{} 180 } 181 } 182 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 183 defer cancel() 184 session := bex.GetSession(ctx) 185 for blobCid := range blobsToGet { 186 _, err := session.GetBlob(ctx, blobCid) 187 suite.Assert().NoError(err) 188 } 189 } 190 } 191 192 func (suite *BlobServiceTestSuite) TestHas() { 193 var blobChans []<-chan blobs.Blob 194 unreceivedBlobs := make([]map[cid.Cid]struct{}, len(suite.blobServices)) 195 196 for i, bex := range suite.blobServices { 197 unreceivedBlobs[i] = make(map[cid.Cid]struct{}) 198 // check that peers are notified when we have a new blob 199 var blobsToGet []cid.Cid 200 for j := 0; j < suite.numNodes; j++ { 201 if j != i { 202 blob := blobs.NewBlob([]byte(fmt.Sprintf("bar%v", i))) 203 blobsToGet = append(blobsToGet, blob.Cid()) 204 unreceivedBlobs[i][blob.Cid()] = struct{}{} 205 } 206 } 207 208 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 209 defer cancel() 210 211 blobs := bex.GetBlobs(ctx, blobsToGet) 212 blobChans = append(blobChans, blobs) 213 } 214 215 // check that blobs are not received until Has is called by the server 216 suite.Require().Never(func() bool { 217 for _, blobChan := range blobChans { 218 select { 219 case _, ok := <-blobChan: 220 if ok { 221 return true 222 } 223 default: 224 } 225 } 226 return false 227 }, time.Second, 100*time.Millisecond) 228 229 for i, bex := range suite.blobServices { 230 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 231 defer cancel() 232 233 err := bex.AddBlob(ctx, blobs.NewBlob([]byte(fmt.Sprintf("bar%v", i)))) 234 suite.Require().NoError(err) 235 } 236 237 for i, blobs := range blobChans { 238 for blob := range blobs { 239 delete(unreceivedBlobs[i], blob.Cid()) 240 } 241 for c := range unreceivedBlobs[i] { 242 suite.T().Errorf("blob %v not received by node %v", c, i) 243 } 244 } 245 }