github.com/decred/dcrlnd@v0.7.6/chanfitness/chaneventstore_test.go (about) 1 package chanfitness 2 3 import ( 4 "errors" 5 "testing" 6 "time" 7 8 "github.com/decred/dcrd/dcrec/secp256k1/v4" 9 "github.com/decred/dcrd/wire" 10 "github.com/decred/dcrlnd/channeldb" 11 "github.com/decred/dcrlnd/clock" 12 "github.com/decred/dcrlnd/routing/route" 13 "github.com/decred/dcrlnd/subscribe" 14 "github.com/stretchr/testify/require" 15 ) 16 17 // testNow is the current time tests will use. 18 var testNow = time.Unix(1592465134, 0) 19 20 // TestStartStoreError tests the starting of the store in cases where the setup 21 // functions fail. It does not test the mechanics of consuming events because 22 // these are covered in a separate set of tests. 23 func TestStartStoreError(t *testing.T) { 24 // Ok and erroring subscribe functions are defined here to de-clutter 25 // tests. 26 okSubscribeFunc := func() (subscribe.Subscription, error) { 27 return newMockSubscription(t), nil 28 } 29 30 errSubscribeFunc := func() (subscribe.Subscription, error) { 31 return nil, errors.New("intentional test err") 32 } 33 34 tests := []struct { 35 name string 36 ChannelEvents func() (subscribe.Subscription, error) 37 PeerEvents func() (subscribe.Subscription, error) 38 GetChannels func() ([]*channeldb.OpenChannel, error) 39 }{ 40 { 41 name: "Channel events fail", 42 ChannelEvents: errSubscribeFunc, 43 }, 44 { 45 name: "Peer events fail", 46 ChannelEvents: okSubscribeFunc, 47 PeerEvents: errSubscribeFunc, 48 }, 49 { 50 name: "Get open channels fails", 51 ChannelEvents: okSubscribeFunc, 52 PeerEvents: okSubscribeFunc, 53 GetChannels: func() ([]*channeldb.OpenChannel, error) { 54 return nil, errors.New("intentional test err") 55 }, 56 }, 57 } 58 59 for _, test := range tests { 60 test := test 61 62 t.Run(test.name, func(t *testing.T) { 63 clock := clock.NewTestClock(testNow) 64 65 store := NewChannelEventStore(&Config{ 66 SubscribeChannelEvents: test.ChannelEvents, 67 SubscribePeerEvents: test.PeerEvents, 68 GetOpenChannels: test.GetChannels, 69 Clock: clock, 70 }) 71 72 err := store.Start() 73 // Check that we receive an error, because the test only 74 // checks for error cases. 75 if err == nil { 76 t.Fatalf("Expected error on startup, got: nil") 77 } 78 }) 79 } 80 } 81 82 // TestMonitorChannelEvents tests the store's handling of channel and peer 83 // events. It tests for the unexpected cases where we receive a channel open for 84 // an already known channel and but does not test for closing an unknown channel 85 // because it would require custom logic in the test to prevent iterating 86 // through an eventLog which does not exist. This test does not test handling 87 // of uptime and lifespan requests, as they are tested in their own tests. 88 func TestMonitorChannelEvents(t *testing.T) { 89 privKey, _ := secp256k1.GeneratePrivateKey() 90 var ( 91 pubKey = privKey.PubKey() 92 chan1 = wire.OutPoint{Index: 1} 93 chan2 = wire.OutPoint{Index: 2} 94 ) 95 96 peer1, err := route.NewVertexFromBytes(pubKey.SerializeCompressed()) 97 require.NoError(t, err) 98 99 t.Run("peer comes online after channel open", func(t *testing.T) { 100 gen := func(ctx *chanEventStoreTestCtx) { 101 ctx.sendChannelOpenedUpdate(pubKey, chan1) 102 ctx.peerEvent(peer1, true) 103 } 104 105 testEventStore(t, gen, peer1, 1) 106 }) 107 108 t.Run("duplicate channel open events", func(t *testing.T) { 109 gen := func(ctx *chanEventStoreTestCtx) { 110 ctx.sendChannelOpenedUpdate(pubKey, chan1) 111 ctx.sendChannelOpenedUpdate(pubKey, chan1) 112 ctx.peerEvent(peer1, true) 113 } 114 115 testEventStore(t, gen, peer1, 1) 116 }) 117 118 t.Run("peer online before channel created", func(t *testing.T) { 119 gen := func(ctx *chanEventStoreTestCtx) { 120 ctx.peerEvent(peer1, true) 121 ctx.sendChannelOpenedUpdate(pubKey, chan1) 122 } 123 124 testEventStore(t, gen, peer1, 1) 125 }) 126 127 t.Run("multiple channels for peer", func(t *testing.T) { 128 gen := func(ctx *chanEventStoreTestCtx) { 129 ctx.peerEvent(peer1, true) 130 ctx.sendChannelOpenedUpdate(pubKey, chan1) 131 132 ctx.peerEvent(peer1, false) 133 ctx.sendChannelOpenedUpdate(pubKey, chan2) 134 } 135 136 testEventStore(t, gen, peer1, 2) 137 }) 138 139 t.Run("multiple channels for peer, one closed", func(t *testing.T) { 140 gen := func(ctx *chanEventStoreTestCtx) { 141 ctx.peerEvent(peer1, true) 142 ctx.sendChannelOpenedUpdate(pubKey, chan1) 143 144 ctx.peerEvent(peer1, false) 145 ctx.sendChannelOpenedUpdate(pubKey, chan2) 146 147 ctx.closeChannel(chan1, pubKey) 148 ctx.peerEvent(peer1, true) 149 } 150 151 testEventStore(t, gen, peer1, 1) 152 }) 153 154 } 155 156 // testEventStore creates a new test contexts, generates a set of events for it 157 // and tests that it has the number of channels we expect. 158 func testEventStore(t *testing.T, generateEvents func(*chanEventStoreTestCtx), 159 peer route.Vertex, expectedChannels int) { 160 161 testCtx := newChanEventStoreTestCtx(t) 162 testCtx.start() 163 164 generateEvents(testCtx) 165 166 // Shutdown the store so that we can safely access the maps in our event 167 // store. 168 testCtx.stop() 169 170 // Get our peer and check that it has the channels we expect. 171 monitor, ok := testCtx.store.peers[peer] 172 require.True(t, ok) 173 174 require.Equal(t, expectedChannels, monitor.channelCount()) 175 } 176 177 // TestStoreFlapCount tests flushing of flap counts to disk on timer ticks and 178 // on store shutdown. 179 func TestStoreFlapCount(t *testing.T) { 180 testCtx := newChanEventStoreTestCtx(t) 181 testCtx.start() 182 183 pubkey, _, _ := testCtx.createChannel() 184 testCtx.peerEvent(pubkey, false) 185 186 // Now, we tick our flap count ticker. We expect our main goroutine to 187 // flush our tick count to disk. 188 testCtx.tickFlapCount() 189 190 // Since we just tracked a offline event, we expect a single flap for 191 // our peer. 192 expectedUpdate := peerFlapCountMap{ 193 pubkey: { 194 Count: 1, 195 LastFlap: testCtx.clock.Now(), 196 }, 197 } 198 199 testCtx.assertFlapCountUpdated() 200 testCtx.assertFlapCountUpdates(expectedUpdate) 201 202 // Create three events for out peer, online/offline/online. 203 testCtx.peerEvent(pubkey, true) 204 testCtx.peerEvent(pubkey, false) 205 testCtx.peerEvent(pubkey, true) 206 207 // Trigger another write. 208 testCtx.tickFlapCount() 209 210 // Since we have processed 3 more events for our peer, we update our 211 // expected online map to have a flap count of 4 for this peer. 212 expectedUpdate[pubkey] = &channeldb.FlapCount{ 213 Count: 4, 214 LastFlap: testCtx.clock.Now(), 215 } 216 testCtx.assertFlapCountUpdated() 217 testCtx.assertFlapCountUpdates(expectedUpdate) 218 219 testCtx.stop() 220 } 221 222 // TestGetChanInfo tests the GetChanInfo function for the cases where a channel 223 // is known and unknown to the store. 224 func TestGetChanInfo(t *testing.T) { 225 ctx := newChanEventStoreTestCtx(t) 226 ctx.start() 227 228 // Make a note of the time that our mocked clock starts on. 229 now := ctx.clock.Now() 230 231 // Create mock vars for a channel but do not add them to our store yet. 232 peer, pk, channel := ctx.newChannel() 233 234 // Send an online event for our peer, although we do not yet have an 235 // open channel. 236 ctx.peerEvent(peer, true) 237 238 // Try to get info for a channel that has not been opened yet, we 239 // expect to get an error. 240 _, err := ctx.store.GetChanInfo(channel, peer) 241 require.Equal(t, ErrChannelNotFound, err) 242 243 // Now we send our store a notification that a channel has been opened. 244 ctx.sendChannelOpenedUpdate(pk, channel) 245 246 // Wait for our channel to be recognized by our store. We need to wait 247 // for the channel to be created so that we do not update our time 248 // before the channel open is processed. 249 require.Eventually(t, func() bool { 250 _, err = ctx.store.GetChanInfo(channel, peer) 251 return err == nil 252 }, timeout, time.Millisecond*20) 253 254 // Increment our test clock by an hour. 255 now = now.Add(time.Hour) 256 ctx.clock.SetTime(now) 257 258 // At this stage our channel has been open and online for an hour. 259 info, err := ctx.store.GetChanInfo(channel, peer) 260 require.NoError(t, err) 261 require.Equal(t, time.Hour, info.Lifetime) 262 require.Equal(t, time.Hour, info.Uptime) 263 264 // Now we send a peer offline event for our channel. 265 ctx.peerEvent(peer, false) 266 267 // Since we have not bumped our mocked time, our uptime calculations 268 // should be the same, even though we've just processed an offline 269 // event. 270 info, err = ctx.store.GetChanInfo(channel, peer) 271 require.NoError(t, err) 272 require.Equal(t, time.Hour, info.Lifetime) 273 require.Equal(t, time.Hour, info.Uptime) 274 275 // Progress our time again. This time, our peer is currently tracked as 276 // being offline, so we expect our channel info to reflect that the peer 277 // has been offline for this period. 278 now = now.Add(time.Hour) 279 ctx.clock.SetTime(now) 280 281 info, err = ctx.store.GetChanInfo(channel, peer) 282 require.NoError(t, err) 283 require.Equal(t, time.Hour*2, info.Lifetime) 284 require.Equal(t, time.Hour, info.Uptime) 285 286 ctx.stop() 287 } 288 289 // TestFlapCount tests querying the store for peer flap counts, covering the 290 // case where the peer is tracked in memory, and the case where we need to 291 // lookup the peer on disk. 292 func TestFlapCount(t *testing.T) { 293 clock := clock.NewTestClock(testNow) 294 295 var ( 296 peer = route.Vertex{9, 9, 9} 297 peerFlapCount = 3 298 lastFlap = clock.Now() 299 ) 300 301 // Create a test context with one peer's flap count already recorded, 302 // which mocks it already having its flap count stored on disk. 303 ctx := newChanEventStoreTestCtx(t) 304 ctx.flapUpdates[peer] = &channeldb.FlapCount{ 305 Count: uint32(peerFlapCount), 306 LastFlap: lastFlap, 307 } 308 309 ctx.start() 310 311 // Create test variables for a peer and channel, but do not add it to 312 // our store yet. 313 peer1 := route.Vertex{1, 2, 3} 314 315 // First, query for a peer that we have no record of in memory or on 316 // disk and confirm that we indicate that the peer was not found. 317 _, ts, err := ctx.store.FlapCount(peer1) 318 require.NoError(t, err) 319 require.Nil(t, ts) 320 321 // Send an online event for our peer. 322 ctx.peerEvent(peer1, true) 323 324 // Assert that we now find a record of the peer with flap count = 1. 325 count, ts, err := ctx.store.FlapCount(peer1) 326 require.NoError(t, err) 327 require.Equal(t, lastFlap, *ts) 328 require.Equal(t, 1, count) 329 330 // Make a request for our peer that not tracked in memory, but does 331 // have its flap count stored on disk. 332 count, ts, err = ctx.store.FlapCount(peer) 333 require.NoError(t, err) 334 require.Equal(t, lastFlap, *ts) 335 require.Equal(t, peerFlapCount, count) 336 337 ctx.stop() 338 }