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  }