github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/bft/state/eventstore/store_service_test.go (about) 1 package eventstore 2 3 import ( 4 "sync" 5 "sync/atomic" 6 "testing" 7 "time" 8 9 "github.com/gnolang/gno/tm2/pkg/bft/types" 10 "github.com/gnolang/gno/tm2/pkg/events" 11 "github.com/stretchr/testify/assert" 12 ) 13 14 // generateTxEvents generates random transaction events 15 func generateTxEvents(count int) []types.EventTx { 16 txEvents := make([]types.EventTx, count) 17 18 for i := 0; i < count; i++ { 19 txEvents[i] = types.EventTx{ 20 Result: types.TxResult{}, 21 } 22 } 23 24 return txEvents 25 } 26 27 func TestEventStoreService_Monitor(t *testing.T) { 28 t.Parallel() 29 30 const defaultTimeout = 5 * time.Second 31 32 var ( 33 startCalled = false 34 stopCalled = false 35 receivedResults = make([]types.TxResult, 0) 36 receivedSize atomic.Int64 37 38 cb events.EventCallback 39 cbSet atomic.Bool 40 41 mockEventStore = &mockEventStore{ 42 startFn: func() error { 43 startCalled = true 44 45 return nil 46 }, 47 stopFn: func() error { 48 stopCalled = true 49 50 return nil 51 }, 52 appendFn: func(result types.TxResult) error { 53 receivedResults = append(receivedResults, result) 54 55 // Atomic because we are accessing this size from a routine 56 receivedSize.Store(int64(len(receivedResults))) 57 58 return nil 59 }, 60 } 61 mockEventSwitch = &mockEventSwitch{ 62 fireEventFn: func(event events.Event) { 63 // Exec the callback on event fire 64 cb(event) 65 }, 66 addListenerFn: func(_ string, callback events.EventCallback) { 67 // Attach callback 68 cb = callback 69 70 // Atomic because we are accessing this info from a routine 71 cbSet.Store(true) 72 }, 73 } 74 ) 75 76 // Create a new event store instance 77 i := NewEventStoreService(mockEventStore, mockEventSwitch) 78 if i == nil { 79 t.Fatal("unable to create event store service") 80 } 81 82 // Start the event store 83 if err := i.OnStart(); err != nil { 84 t.Fatalf("unable to start event store, %v", err) 85 } 86 87 assert.True(t, startCalled) 88 89 t.Cleanup(func() { 90 // Stop the event store 91 i.OnStop() 92 93 assert.True(t, stopCalled) 94 }) 95 96 // Fire off the events so the event store can catch them 97 numEvents := 1000 98 txEvents := generateTxEvents(numEvents) 99 100 var wg sync.WaitGroup 101 102 // Start a routine that asynchronously pushes events 103 wg.Add(1) 104 go func() { 105 defer wg.Done() 106 107 timeout := time.After(defaultTimeout) 108 109 for { 110 select { 111 case <-timeout: 112 return 113 default: 114 // If the callback is set, fire the events 115 if !cbSet.Load() { 116 // Listener not set yet 117 continue 118 } 119 120 for _, event := range txEvents { 121 mockEventSwitch.FireEvent(event) 122 } 123 124 return 125 } 126 } 127 }() 128 129 // Start a routine that monitors received results 130 wg.Add(1) 131 go func() { 132 defer wg.Done() 133 134 timeout := time.After(defaultTimeout) 135 136 for { 137 select { 138 case <-timeout: 139 return 140 default: 141 if int(receivedSize.Load()) == numEvents { 142 return 143 } 144 } 145 } 146 }() 147 148 wg.Wait() 149 150 // Make sure all results were received 151 if len(receivedResults) != numEvents { 152 t.Fatalf("invalid number of results received, %d", len(receivedResults)) 153 } 154 155 // Make sure all results match 156 for index, event := range txEvents { 157 assert.Equal(t, event.Result, receivedResults[index]) 158 } 159 }