github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/p2p/scoring/subscription_provider_test.go (about) 1 package scoring_test 2 3 import ( 4 "context" 5 "testing" 6 "time" 7 8 "github.com/libp2p/go-libp2p/core/peer" 9 "github.com/stretchr/testify/assert" 10 mockery "github.com/stretchr/testify/mock" 11 "github.com/stretchr/testify/require" 12 13 "github.com/onflow/flow-go/config" 14 "github.com/onflow/flow-go/model/flow" 15 "github.com/onflow/flow-go/module/irrecoverable" 16 "github.com/onflow/flow-go/module/metrics" 17 "github.com/onflow/flow-go/module/mock" 18 "github.com/onflow/flow-go/network" 19 "github.com/onflow/flow-go/network/p2p" 20 mockp2p "github.com/onflow/flow-go/network/p2p/mock" 21 "github.com/onflow/flow-go/network/p2p/scoring" 22 "github.com/onflow/flow-go/utils/slices" 23 "github.com/onflow/flow-go/utils/unittest" 24 ) 25 26 // TestSubscriptionProvider_GetSubscribedTopics tests that the SubscriptionProvider returns the correct 27 // list of topics a peer is subscribed to. 28 func TestSubscriptionProvider_GetSubscribedTopics(t *testing.T) { 29 tp := mockp2p.NewTopicProvider(t) 30 cfg, err := config.DefaultConfig() 31 require.NoError(t, err) 32 idProvider := mock.NewIdentityProvider(t) 33 34 // set a low update interval to speed up the test 35 cfg.NetworkConfig.GossipSub.SubscriptionProvider.UpdateInterval = 100 * time.Millisecond 36 37 sp, err := scoring.NewSubscriptionProvider(&scoring.SubscriptionProviderConfig{ 38 Logger: unittest.Logger(), 39 TopicProviderOracle: func() p2p.TopicProvider { 40 return tp 41 }, 42 Params: &cfg.NetworkConfig.GossipSub.SubscriptionProvider, 43 HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), 44 IdProvider: idProvider, 45 NetworkingType: network.PrivateNetwork, 46 }) 47 require.NoError(t, err) 48 49 tp.On("GetTopics").Return([]string{"topic1", "topic2", "topic3"}).Maybe() 50 51 peer1 := unittest.PeerIdFixture(t) 52 peer2 := unittest.PeerIdFixture(t) 53 peer3 := unittest.PeerIdFixture(t) 54 55 idProvider.On("ByPeerID", mockery.Anything).Return(unittest.IdentityFixture(), true).Maybe() 56 57 // mock peers 1 and 2 subscribed to topic 1 (along with other random peers) 58 tp.On("ListPeers", "topic1").Return(append([]peer.ID{peer1, peer2}, unittest.PeerIdFixtures(t, 10)...)) 59 // mock peers 2 and 3 subscribed to topic 2 (along with other random peers) 60 tp.On("ListPeers", "topic2").Return(append([]peer.ID{peer2, peer3}, unittest.PeerIdFixtures(t, 10)...)) 61 // mock peers 1 and 3 subscribed to topic 3 (along with other random peers) 62 tp.On("ListPeers", "topic3").Return(append([]peer.ID{peer1, peer3}, unittest.PeerIdFixtures(t, 10)...)) 63 64 ctx, cancel := context.WithCancel(context.Background()) 65 defer func() { 66 cancel() 67 unittest.RequireCloseBefore(t, sp.Done(), 1*time.Second, "subscription provider did not stop in time") 68 }() 69 signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) 70 sp.Start(signalerCtx) 71 unittest.RequireCloseBefore(t, sp.Ready(), 1*time.Second, "subscription provider did not start in time") 72 73 // As the calls to the TopicProvider are asynchronous, we need to wait for the goroutines to finish. 74 assert.Eventually(t, func() bool { 75 return slices.AreStringSlicesEqual([]string{"topic1", "topic3"}, sp.GetSubscribedTopics(peer1)) 76 }, 1*time.Second, 100*time.Millisecond) 77 78 assert.Eventually(t, func() bool { 79 return slices.AreStringSlicesEqual([]string{"topic1", "topic2"}, sp.GetSubscribedTopics(peer2)) 80 }, 1*time.Second, 100*time.Millisecond) 81 82 assert.Eventually(t, func() bool { 83 return slices.AreStringSlicesEqual([]string{"topic2", "topic3"}, sp.GetSubscribedTopics(peer3)) 84 }, 1*time.Second, 100*time.Millisecond) 85 } 86 87 // TestSubscriptionProvider_GetSubscribedTopics_SkippingUnknownPeers tests that the SubscriptionProvider skips 88 // unknown peers when returning the list of topics a peer is subscribed to. In other words, if a peer is unknown, 89 // the SubscriptionProvider should not keep track of its subscriptions. 90 func TestSubscriptionProvider_GetSubscribedTopics_SkippingUnknownPeers(t *testing.T) { 91 tp := mockp2p.NewTopicProvider(t) 92 cfg, err := config.DefaultConfig() 93 require.NoError(t, err) 94 idProvider := mock.NewIdentityProvider(t) 95 96 // set a low update interval to speed up the test 97 cfg.NetworkConfig.GossipSub.SubscriptionProvider.UpdateInterval = 100 * time.Millisecond 98 99 sp, err := scoring.NewSubscriptionProvider(&scoring.SubscriptionProviderConfig{ 100 Logger: unittest.Logger(), 101 TopicProviderOracle: func() p2p.TopicProvider { 102 return tp 103 }, 104 Params: &cfg.NetworkConfig.GossipSub.SubscriptionProvider, 105 HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(), 106 IdProvider: idProvider, 107 NetworkingType: network.PrivateNetwork, 108 }) 109 require.NoError(t, err) 110 111 tp.On("GetTopics").Return([]string{"topic1", "topic2", "topic3"}).Maybe() 112 113 peer1 := unittest.PeerIdFixture(t) 114 peer2 := unittest.PeerIdFixture(t) 115 peer3 := unittest.PeerIdFixture(t) 116 117 // mock peers 1 and 2 as a known peer; peer 3 as an unknown peer 118 idProvider.On("ByPeerID", mockery.Anything). 119 Return(func(pid peer.ID) *flow.Identity { 120 if pid == peer1 || pid == peer2 { 121 return unittest.IdentityFixture() 122 } 123 return nil 124 }, func(pid peer.ID) bool { 125 if pid == peer1 || pid == peer2 { 126 return true 127 } 128 return false 129 }).Maybe() 130 131 // mock peers 1 and 2 subscribed to topic 1 (along with other random peers) 132 tp.On("ListPeers", "topic1").Return(append([]peer.ID{peer1, peer2}, unittest.PeerIdFixtures(t, 10)...)) 133 // mock peers 2 and 3 subscribed to topic 2 (along with other random peers) 134 tp.On("ListPeers", "topic2").Return(append([]peer.ID{peer2, peer3}, unittest.PeerIdFixtures(t, 10)...)) 135 // mock peers 1 and 3 subscribed to topic 3 (along with other random peers) 136 tp.On("ListPeers", "topic3").Return(append([]peer.ID{peer1, peer3}, unittest.PeerIdFixtures(t, 10)...)) 137 138 ctx, cancel := context.WithCancel(context.Background()) 139 defer func() { 140 cancel() 141 unittest.RequireCloseBefore(t, sp.Done(), 1*time.Second, "subscription provider did not stop in time") 142 }() 143 signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) 144 sp.Start(signalerCtx) 145 unittest.RequireCloseBefore(t, sp.Ready(), 1*time.Second, "subscription provider did not start in time") 146 147 // As the calls to the TopicProvider are asynchronous, we need to wait for the goroutines to finish. 148 // peer 1 should be eventually subscribed to topic 1 and topic 3; while peer 3 should not have any subscriptions record since it is unknown 149 assert.Eventually(t, func() bool { 150 return slices.AreStringSlicesEqual([]string{"topic1", "topic3"}, sp.GetSubscribedTopics(peer1)) && 151 slices.AreStringSlicesEqual([]string{}, sp.GetSubscribedTopics(peer3)) 152 }, 1*time.Second, 100*time.Millisecond) 153 154 // peer 2 should be eventually subscribed to topic 1 and topic 2; while peer 3 should not have any subscriptions record since it is unknown 155 assert.Eventually(t, func() bool { 156 return slices.AreStringSlicesEqual([]string{"topic1", "topic2"}, sp.GetSubscribedTopics(peer2)) && 157 slices.AreStringSlicesEqual([]string{}, sp.GetSubscribedTopics(peer3)) 158 }, 1*time.Second, 100*time.Millisecond) 159 }