github.com/status-im/status-go@v1.1.0/services/subscriptions/subscriptions_test.go (about)

     1  package subscriptions
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"sync"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/status-im/status-go/signal"
    14  )
    15  
    16  const (
    17  	filterID = "123"
    18  	filterNS = "tst"
    19  )
    20  
    21  type mockFilter struct {
    22  	sync.Mutex
    23  	filterID       string
    24  	data           []interface{}
    25  	filterError    error
    26  	uninstalled    bool
    27  	uninstallError error
    28  }
    29  
    30  func newMockFilter(filterID string) *mockFilter {
    31  	return &mockFilter{
    32  		filterID: filterID,
    33  	}
    34  }
    35  
    36  func (mf *mockFilter) getID() string {
    37  	mf.Lock()
    38  	defer mf.Unlock()
    39  	return mf.filterID
    40  }
    41  func (mf *mockFilter) getChanges() ([]interface{}, error) {
    42  	mf.Lock()
    43  	defer mf.Unlock()
    44  
    45  	if mf.filterError != nil {
    46  		err := mf.filterError
    47  		mf.filterError = nil
    48  		return nil, err
    49  	}
    50  
    51  	data := mf.data
    52  	mf.data = nil
    53  	return data, nil
    54  }
    55  
    56  func (mf *mockFilter) uninstall() error {
    57  	mf.Lock()
    58  	defer mf.Unlock()
    59  	mf.uninstalled = true
    60  	return mf.uninstallError
    61  }
    62  
    63  func (mf *mockFilter) setData(data ...interface{}) {
    64  	mf.Lock()
    65  	defer mf.Unlock()
    66  	mf.data = data
    67  }
    68  
    69  func (mf *mockFilter) setError(err error) {
    70  	mf.Lock()
    71  	defer mf.Unlock()
    72  	mf.data = nil
    73  	mf.filterError = err
    74  }
    75  
    76  func TestSubscriptionGetData(t *testing.T) {
    77  	filter := newMockFilter(filterID)
    78  
    79  	subs := NewSubscriptions(time.Microsecond)
    80  
    81  	subID, _ := subs.Create(filterNS, filter)
    82  
    83  	require.Equal(t, string(subID), fmt.Sprintf("%s-%s", filterNS, filterID))
    84  
    85  	proceed := make(chan struct{})
    86  
    87  	signal.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
    88  		defer close(proceed)
    89  		validateFilterData(t, jsonEvent, string(subID), "1", "2", "3", "4")
    90  	})
    91  
    92  	filter.setData("1", "2", "3", "4")
    93  
    94  	select {
    95  	case <-proceed:
    96  		return
    97  	case <-time.After(time.Second):
    98  		require.NoError(t, errors.New("timeout while waiting for filter results"))
    99  	}
   100  
   101  	require.NoError(t, subs.removeAll())
   102  	signal.ResetDefaultNodeNotificationHandler()
   103  }
   104  
   105  func TestSubscriptionGetError(t *testing.T) {
   106  	filter := newMockFilter(filterID)
   107  
   108  	subs := NewSubscriptions(time.Microsecond)
   109  
   110  	subID, _ := subs.Create(filterNS, filter)
   111  
   112  	require.Equal(t, string(subID), fmt.Sprintf("%s-%s", filterNS, filterID))
   113  
   114  	proceed := make(chan struct{})
   115  
   116  	expectedError := errors.New("test-error")
   117  
   118  	signal.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
   119  		defer close(proceed)
   120  		validateFilterError(t, jsonEvent, string(subID), expectedError.Error())
   121  	})
   122  
   123  	filter.setError(expectedError)
   124  
   125  	select {
   126  	case <-proceed:
   127  		return
   128  	case <-time.After(time.Second):
   129  		require.NoError(t, errors.New("timeout while waiting for filter results"))
   130  	}
   131  
   132  	require.NoError(t, subs.removeAll())
   133  	signal.ResetDefaultNodeNotificationHandler()
   134  }
   135  
   136  func TestSubscriptionRemove(t *testing.T) {
   137  	filter := newMockFilter(filterID)
   138  	subs := NewSubscriptions(time.Microsecond)
   139  
   140  	subID, err := subs.Create(filterNS, filter)
   141  	require.NoError(t, err)
   142  	time.Sleep(time.Millisecond * 100) // create starts in a goroutine
   143  
   144  	require.NoError(t, subs.Remove(subID))
   145  	require.True(t, filter.uninstalled)
   146  	require.Empty(t, subs.subs)
   147  }
   148  
   149  func TestSubscriptionRemoveError(t *testing.T) {
   150  	filter := newMockFilter(filterID)
   151  	filter.uninstallError = errors.New("uninstall-error-1")
   152  
   153  	subs := NewSubscriptions(time.Microsecond)
   154  	subID, err := subs.Create(filterNS, filter)
   155  	require.NoError(t, err)
   156  	time.Sleep(time.Millisecond * 100) // create starts in a goroutine
   157  
   158  	require.Equal(t, subs.Remove(subID), filter.uninstallError)
   159  	require.True(t, filter.uninstalled)
   160  	require.Equal(t, len(subs.subs), 0)
   161  }
   162  
   163  func TestSubscriptionRemoveAll(t *testing.T) {
   164  	filter0 := newMockFilter(filterID)
   165  	filter1 := newMockFilter(filterID + "1")
   166  
   167  	subs := NewSubscriptions(time.Microsecond)
   168  	_, err := subs.Create(filterNS, filter0)
   169  	require.NoError(t, err)
   170  	_, err = subs.Create(filterNS, filter1)
   171  	require.NoError(t, err)
   172  	time.Sleep(time.Millisecond * 100) // create starts in a goroutine
   173  
   174  	require.Equal(t, len(subs.subs), 2)
   175  	err = subs.removeAll()
   176  	require.NoError(t, err)
   177  	require.False(t, filter0.uninstalled)
   178  	require.False(t, filter1.uninstalled)
   179  	require.Equal(t, len(subs.subs), 0)
   180  }
   181  
   182  func validateFilterError(t *testing.T, jsonEvent string, expectedSubID string, expectedErrorMessage string) {
   183  	result := struct {
   184  		Event signal.SubscriptionErrorEvent `json:"event"`
   185  		Type  string                        `json:"type"`
   186  	}{}
   187  	require.NoError(t, json.Unmarshal([]byte(jsonEvent), &result))
   188  	require.Equal(t, signal.EventSubscriptionsError, result.Type)
   189  	require.Equal(t, expectedErrorMessage, result.Event.ErrorMessage)
   190  }
   191  
   192  func validateFilterData(t *testing.T, jsonEvent string, expectedSubID string, expectedData ...interface{}) {
   193  	result := struct {
   194  		Event signal.SubscriptionDataEvent `json:"event"`
   195  		Type  string                       `json:"type"`
   196  	}{}
   197  	require.NoError(t, json.Unmarshal([]byte(jsonEvent), &result))
   198  	require.Equal(t, signal.EventSubscriptionsData, result.Type)
   199  	require.Equal(t, expectedData, result.Event.Data)
   200  	require.Equal(t, expectedSubID, result.Event.FilterID)
   201  }