github.com/number571/tendermint@v0.34.11-gost/libs/pubsub/pubsub_test.go (about)

     1  package pubsub_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"runtime/debug"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	abci "github.com/number571/tendermint/abci/types"
    14  	"github.com/number571/tendermint/libs/log"
    15  
    16  	"github.com/number571/tendermint/libs/pubsub"
    17  	"github.com/number571/tendermint/libs/pubsub/query"
    18  )
    19  
    20  const (
    21  	clientID = "test-client"
    22  )
    23  
    24  func TestSubscribe(t *testing.T) {
    25  	s := pubsub.NewServer()
    26  	s.SetLogger(log.TestingLogger())
    27  	err := s.Start()
    28  	require.NoError(t, err)
    29  	t.Cleanup(func() {
    30  		if err := s.Stop(); err != nil {
    31  			t.Error(err)
    32  		}
    33  	})
    34  
    35  	ctx := context.Background()
    36  	subscription, err := s.Subscribe(ctx, clientID, query.Empty{})
    37  	require.NoError(t, err)
    38  
    39  	require.Equal(t, 1, s.NumClients())
    40  	require.Equal(t, 1, s.NumClientSubscriptions(clientID))
    41  
    42  	err = s.Publish(ctx, "Ka-Zar")
    43  	require.NoError(t, err)
    44  	assertReceive(t, "Ka-Zar", subscription.Out())
    45  
    46  	published := make(chan struct{})
    47  	go func() {
    48  		defer close(published)
    49  
    50  		err := s.Publish(ctx, "Quicksilver")
    51  		require.NoError(t, err)
    52  
    53  		err = s.Publish(ctx, "Asylum")
    54  		require.NoError(t, err)
    55  
    56  		err = s.Publish(ctx, "Ivan")
    57  		require.NoError(t, err)
    58  	}()
    59  
    60  	select {
    61  	case <-published:
    62  		assertReceive(t, "Quicksilver", subscription.Out())
    63  		assertCanceled(t, subscription, pubsub.ErrOutOfCapacity)
    64  	case <-time.After(3 * time.Second):
    65  		t.Fatal("Expected Publish(Asylum) not to block")
    66  	}
    67  }
    68  
    69  func TestSubscribeWithCapacity(t *testing.T) {
    70  	s := pubsub.NewServer()
    71  	s.SetLogger(log.TestingLogger())
    72  	err := s.Start()
    73  	require.NoError(t, err)
    74  	t.Cleanup(func() {
    75  		if err := s.Stop(); err != nil {
    76  			t.Error(err)
    77  		}
    78  	})
    79  
    80  	ctx := context.Background()
    81  	require.Panics(t, func() {
    82  		_, err = s.Subscribe(ctx, clientID, query.Empty{}, -1)
    83  		require.NoError(t, err)
    84  	})
    85  	require.Panics(t, func() {
    86  		_, err = s.Subscribe(ctx, clientID, query.Empty{}, 0)
    87  		require.NoError(t, err)
    88  	})
    89  	subscription, err := s.Subscribe(ctx, clientID, query.Empty{}, 1)
    90  	require.NoError(t, err)
    91  	err = s.Publish(ctx, "Aggamon")
    92  	require.NoError(t, err)
    93  	assertReceive(t, "Aggamon", subscription.Out())
    94  }
    95  
    96  func TestSubscribeUnbuffered(t *testing.T) {
    97  	s := pubsub.NewServer()
    98  	s.SetLogger(log.TestingLogger())
    99  	err := s.Start()
   100  	require.NoError(t, err)
   101  	t.Cleanup(func() {
   102  		if err := s.Stop(); err != nil {
   103  			t.Error(err)
   104  		}
   105  	})
   106  
   107  	ctx := context.Background()
   108  	subscription, err := s.SubscribeUnbuffered(ctx, clientID, query.Empty{})
   109  	require.NoError(t, err)
   110  
   111  	published := make(chan struct{})
   112  	go func() {
   113  		defer close(published)
   114  
   115  		err := s.Publish(ctx, "Ultron")
   116  		require.NoError(t, err)
   117  
   118  		err = s.Publish(ctx, "Darkhawk")
   119  		require.NoError(t, err)
   120  	}()
   121  
   122  	select {
   123  	case <-published:
   124  		t.Fatal("Expected Publish(Darkhawk) to block")
   125  	case <-time.After(3 * time.Second):
   126  		assertReceive(t, "Ultron", subscription.Out())
   127  		assertReceive(t, "Darkhawk", subscription.Out())
   128  	}
   129  }
   130  
   131  func TestSlowClientIsRemovedWithErrOutOfCapacity(t *testing.T) {
   132  	s := pubsub.NewServer()
   133  	s.SetLogger(log.TestingLogger())
   134  	err := s.Start()
   135  	require.NoError(t, err)
   136  	t.Cleanup(func() {
   137  		if err := s.Stop(); err != nil {
   138  			t.Error(err)
   139  		}
   140  	})
   141  
   142  	ctx := context.Background()
   143  	subscription, err := s.Subscribe(ctx, clientID, query.Empty{})
   144  	require.NoError(t, err)
   145  	err = s.Publish(ctx, "Fat Cobra")
   146  	require.NoError(t, err)
   147  	err = s.Publish(ctx, "Viper")
   148  	require.NoError(t, err)
   149  
   150  	assertCanceled(t, subscription, pubsub.ErrOutOfCapacity)
   151  }
   152  
   153  func TestDifferentClients(t *testing.T) {
   154  	s := pubsub.NewServer()
   155  	s.SetLogger(log.TestingLogger())
   156  
   157  	require.NoError(t, s.Start())
   158  	t.Cleanup(func() {
   159  		if err := s.Stop(); err != nil {
   160  			t.Error(err)
   161  		}
   162  	})
   163  
   164  	ctx := context.Background()
   165  
   166  	subscription1, err := s.Subscribe(ctx, "client-1", query.MustParse("tm.events.type='NewBlock'"))
   167  	require.NoError(t, err)
   168  
   169  	events := []abci.Event{
   170  		{
   171  			Type:       "tm.events",
   172  			Attributes: []abci.EventAttribute{{Key: "type", Value: "NewBlock"}},
   173  		},
   174  	}
   175  
   176  	require.NoError(t, s.PublishWithEvents(ctx, "Iceman", events))
   177  	assertReceive(t, "Iceman", subscription1.Out())
   178  
   179  	subscription2, err := s.Subscribe(
   180  		ctx,
   181  		"client-2",
   182  		query.MustParse("tm.events.type='NewBlock' AND abci.account.name='Igor'"),
   183  	)
   184  	require.NoError(t, err)
   185  
   186  	events = []abci.Event{
   187  		{
   188  			Type:       "tm.events",
   189  			Attributes: []abci.EventAttribute{{Key: "type", Value: "NewBlock"}},
   190  		},
   191  		{
   192  			Type:       "abci.account",
   193  			Attributes: []abci.EventAttribute{{Key: "name", Value: "Igor"}},
   194  		},
   195  	}
   196  
   197  	require.NoError(t, s.PublishWithEvents(ctx, "Ultimo", events))
   198  	assertReceive(t, "Ultimo", subscription1.Out())
   199  	assertReceive(t, "Ultimo", subscription2.Out())
   200  
   201  	subscription3, err := s.Subscribe(
   202  		ctx,
   203  		"client-3",
   204  		query.MustParse("tm.events.type='NewRoundStep' AND abci.account.name='Igor' AND abci.invoice.number = 10"),
   205  	)
   206  	require.NoError(t, err)
   207  
   208  	events = []abci.Event{
   209  		{
   210  			Type:       "tm.events",
   211  			Attributes: []abci.EventAttribute{{Key: "type", Value: "NewRoundStep"}},
   212  		},
   213  	}
   214  
   215  	require.NoError(t, s.PublishWithEvents(ctx, "Valeria Richards", events))
   216  	require.Zero(t, len(subscription3.Out()))
   217  }
   218  
   219  func TestSubscribeDuplicateKeys(t *testing.T) {
   220  	ctx := context.Background()
   221  	s := pubsub.NewServer()
   222  	s.SetLogger(log.TestingLogger())
   223  
   224  	require.NoError(t, s.Start())
   225  
   226  	t.Cleanup(func() {
   227  		if err := s.Stop(); err != nil {
   228  			t.Error(err)
   229  		}
   230  	})
   231  
   232  	testCases := []struct {
   233  		query    string
   234  		expected interface{}
   235  	}{
   236  		{
   237  			"withdraw.rewards='17'",
   238  			"Iceman",
   239  		},
   240  		{
   241  			"withdraw.rewards='22'",
   242  			"Iceman",
   243  		},
   244  		{
   245  			"withdraw.rewards='1' AND withdraw.rewards='22'",
   246  			"Iceman",
   247  		},
   248  		{
   249  			"withdraw.rewards='100'",
   250  			nil,
   251  		},
   252  	}
   253  
   254  	for i, tc := range testCases {
   255  		sub, err := s.Subscribe(ctx, fmt.Sprintf("client-%d", i), query.MustParse(tc.query))
   256  		require.NoError(t, err)
   257  
   258  		events := []abci.Event{
   259  			{
   260  				Type: "transfer",
   261  				Attributes: []abci.EventAttribute{
   262  					{Key: "sender", Value: "foo"},
   263  					{Key: "sender", Value: "bar"},
   264  					{Key: "sender", Value: "baz"},
   265  				},
   266  			},
   267  			{
   268  				Type: "withdraw",
   269  				Attributes: []abci.EventAttribute{
   270  					{Key: "rewards", Value: "1"},
   271  					{Key: "rewards", Value: "17"},
   272  					{Key: "rewards", Value: "22"},
   273  				},
   274  			},
   275  		}
   276  
   277  		require.NoError(t, s.PublishWithEvents(ctx, "Iceman", events))
   278  
   279  		if tc.expected != nil {
   280  			assertReceive(t, tc.expected, sub.Out())
   281  		} else {
   282  			require.Zero(t, len(sub.Out()))
   283  		}
   284  	}
   285  }
   286  
   287  func TestClientSubscribesTwice(t *testing.T) {
   288  	s := pubsub.NewServer()
   289  	s.SetLogger(log.TestingLogger())
   290  	err := s.Start()
   291  	require.NoError(t, err)
   292  	t.Cleanup(func() {
   293  		if err := s.Stop(); err != nil {
   294  			t.Error(err)
   295  		}
   296  	})
   297  
   298  	ctx := context.Background()
   299  	q := query.MustParse("tm.events.type='NewBlock'")
   300  
   301  	subscription1, err := s.Subscribe(ctx, clientID, q)
   302  	require.NoError(t, err)
   303  
   304  	events := []abci.Event{
   305  		{
   306  			Type:       "tm.events",
   307  			Attributes: []abci.EventAttribute{{Key: "type", Value: "NewBlock"}},
   308  		},
   309  	}
   310  
   311  	require.NoError(t, s.PublishWithEvents(ctx, "Goblin Queen", events))
   312  	assertReceive(t, "Goblin Queen", subscription1.Out())
   313  
   314  	subscription2, err := s.Subscribe(ctx, clientID, q)
   315  	require.Error(t, err)
   316  	require.Nil(t, subscription2)
   317  
   318  	require.NoError(t, s.PublishWithEvents(ctx, "Spider-Man", events))
   319  	assertReceive(t, "Spider-Man", subscription1.Out())
   320  }
   321  
   322  func TestUnsubscribe(t *testing.T) {
   323  	s := pubsub.NewServer()
   324  	s.SetLogger(log.TestingLogger())
   325  	err := s.Start()
   326  	require.NoError(t, err)
   327  	t.Cleanup(func() {
   328  		if err := s.Stop(); err != nil {
   329  			t.Error(err)
   330  		}
   331  	})
   332  
   333  	ctx := context.Background()
   334  	subscription, err := s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'"))
   335  	require.NoError(t, err)
   336  	err = s.Unsubscribe(ctx, pubsub.UnsubscribeArgs{
   337  		Subscriber: clientID,
   338  		Query:      query.MustParse("tm.events.type='NewBlock'")})
   339  	require.NoError(t, err)
   340  
   341  	err = s.Publish(ctx, "Nick Fury")
   342  	require.NoError(t, err)
   343  	require.Zero(t, len(subscription.Out()), "Should not receive anything after Unsubscribe")
   344  
   345  	assertCanceled(t, subscription, pubsub.ErrUnsubscribed)
   346  }
   347  
   348  func TestClientUnsubscribesTwice(t *testing.T) {
   349  	s := pubsub.NewServer()
   350  	s.SetLogger(log.TestingLogger())
   351  	err := s.Start()
   352  	require.NoError(t, err)
   353  	t.Cleanup(func() {
   354  		if err := s.Stop(); err != nil {
   355  			t.Error(err)
   356  		}
   357  	})
   358  
   359  	ctx := context.Background()
   360  	_, err = s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'"))
   361  	require.NoError(t, err)
   362  	err = s.Unsubscribe(ctx, pubsub.UnsubscribeArgs{
   363  		Subscriber: clientID,
   364  		Query:      query.MustParse("tm.events.type='NewBlock'")})
   365  	require.NoError(t, err)
   366  
   367  	err = s.Unsubscribe(ctx, pubsub.UnsubscribeArgs{
   368  		Subscriber: clientID,
   369  		Query:      query.MustParse("tm.events.type='NewBlock'")})
   370  	require.Equal(t, pubsub.ErrSubscriptionNotFound, err)
   371  	err = s.UnsubscribeAll(ctx, clientID)
   372  	require.Equal(t, pubsub.ErrSubscriptionNotFound, err)
   373  }
   374  
   375  func TestResubscribe(t *testing.T) {
   376  	s := pubsub.NewServer()
   377  	s.SetLogger(log.TestingLogger())
   378  	err := s.Start()
   379  	require.NoError(t, err)
   380  	t.Cleanup(func() {
   381  		if err := s.Stop(); err != nil {
   382  			t.Error(err)
   383  		}
   384  	})
   385  
   386  	ctx := context.Background()
   387  	_, err = s.Subscribe(ctx, clientID, query.Empty{})
   388  	require.NoError(t, err)
   389  	err = s.Unsubscribe(ctx, pubsub.UnsubscribeArgs{Subscriber: clientID, Query: query.Empty{}})
   390  	require.NoError(t, err)
   391  	subscription, err := s.Subscribe(ctx, clientID, query.Empty{})
   392  	require.NoError(t, err)
   393  
   394  	err = s.Publish(ctx, "Cable")
   395  	require.NoError(t, err)
   396  	assertReceive(t, "Cable", subscription.Out())
   397  }
   398  
   399  func TestUnsubscribeAll(t *testing.T) {
   400  	s := pubsub.NewServer()
   401  	s.SetLogger(log.TestingLogger())
   402  	err := s.Start()
   403  	require.NoError(t, err)
   404  	t.Cleanup(func() {
   405  		if err := s.Stop(); err != nil {
   406  			t.Error(err)
   407  		}
   408  	})
   409  
   410  	ctx := context.Background()
   411  	subscription1, err := s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'"))
   412  	require.NoError(t, err)
   413  	subscription2, err := s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlockHeader'"))
   414  	require.NoError(t, err)
   415  
   416  	err = s.UnsubscribeAll(ctx, clientID)
   417  	require.NoError(t, err)
   418  
   419  	err = s.Publish(ctx, "Nick Fury")
   420  	require.NoError(t, err)
   421  	require.Zero(t, len(subscription1.Out()), "Should not receive anything after UnsubscribeAll")
   422  	require.Zero(t, len(subscription2.Out()), "Should not receive anything after UnsubscribeAll")
   423  
   424  	assertCanceled(t, subscription1, pubsub.ErrUnsubscribed)
   425  	assertCanceled(t, subscription2, pubsub.ErrUnsubscribed)
   426  }
   427  
   428  func TestBufferCapacity(t *testing.T) {
   429  	s := pubsub.NewServer(pubsub.BufferCapacity(2))
   430  	s.SetLogger(log.TestingLogger())
   431  
   432  	require.Equal(t, 2, s.BufferCapacity())
   433  
   434  	ctx := context.Background()
   435  	err := s.Publish(ctx, "Nighthawk")
   436  	require.NoError(t, err)
   437  	err = s.Publish(ctx, "Sage")
   438  	require.NoError(t, err)
   439  
   440  	ctx, cancel := context.WithTimeout(ctx, 10*time.Millisecond)
   441  	defer cancel()
   442  
   443  	err = s.Publish(ctx, "Ironclad")
   444  	if assert.Error(t, err) {
   445  		require.Equal(t, context.DeadlineExceeded, err)
   446  	}
   447  }
   448  
   449  func Benchmark10Clients(b *testing.B)   { benchmarkNClients(10, b) }
   450  func Benchmark100Clients(b *testing.B)  { benchmarkNClients(100, b) }
   451  func Benchmark1000Clients(b *testing.B) { benchmarkNClients(1000, b) }
   452  
   453  func Benchmark10ClientsOneQuery(b *testing.B)   { benchmarkNClientsOneQuery(10, b) }
   454  func Benchmark100ClientsOneQuery(b *testing.B)  { benchmarkNClientsOneQuery(100, b) }
   455  func Benchmark1000ClientsOneQuery(b *testing.B) { benchmarkNClientsOneQuery(1000, b) }
   456  
   457  func benchmarkNClients(n int, b *testing.B) {
   458  	s := pubsub.NewServer()
   459  	err := s.Start()
   460  	require.NoError(b, err)
   461  
   462  	b.Cleanup(func() {
   463  		if err := s.Stop(); err != nil {
   464  			b.Error(err)
   465  		}
   466  	})
   467  
   468  	ctx := context.Background()
   469  	for i := 0; i < n; i++ {
   470  		subscription, err := s.Subscribe(
   471  			ctx,
   472  			clientID,
   473  			query.MustParse(fmt.Sprintf("abci.Account.Owner = 'Ivan' AND abci.Invoices.Number = %d", i)),
   474  		)
   475  		if err != nil {
   476  			b.Fatal(err)
   477  		}
   478  		go func() {
   479  			for {
   480  				select {
   481  				case <-subscription.Out():
   482  					continue
   483  				case <-subscription.Canceled():
   484  					return
   485  				}
   486  			}
   487  		}()
   488  	}
   489  
   490  	b.ReportAllocs()
   491  	b.ResetTimer()
   492  	for i := 0; i < b.N; i++ {
   493  		events := []abci.Event{
   494  			{
   495  				Type:       "abci.Account",
   496  				Attributes: []abci.EventAttribute{{Key: "Owner", Value: "Ivan"}},
   497  			},
   498  			{
   499  				Type:       "abci.Invoices",
   500  				Attributes: []abci.EventAttribute{{Key: "Number", Value: string(rune(i))}},
   501  			},
   502  		}
   503  
   504  		require.NoError(b, s.PublishWithEvents(ctx, "Gamora", events))
   505  	}
   506  }
   507  
   508  func benchmarkNClientsOneQuery(n int, b *testing.B) {
   509  	s := pubsub.NewServer()
   510  	err := s.Start()
   511  	require.NoError(b, err)
   512  	b.Cleanup(func() {
   513  		if err := s.Stop(); err != nil {
   514  			b.Error(err)
   515  		}
   516  	})
   517  
   518  	ctx := context.Background()
   519  	q := query.MustParse("abci.Account.Owner = 'Ivan' AND abci.Invoices.Number = 1")
   520  	for i := 0; i < n; i++ {
   521  		subscription, err := s.Subscribe(ctx, clientID, q)
   522  		if err != nil {
   523  			b.Fatal(err)
   524  		}
   525  		go func() {
   526  			for {
   527  				select {
   528  				case <-subscription.Out():
   529  					continue
   530  				case <-subscription.Canceled():
   531  					return
   532  				}
   533  			}
   534  		}()
   535  	}
   536  
   537  	b.ReportAllocs()
   538  	b.ResetTimer()
   539  
   540  	for i := 0; i < b.N; i++ {
   541  		events := []abci.Event{
   542  			{
   543  				Type:       "abci.Account",
   544  				Attributes: []abci.EventAttribute{{Key: "Owner", Value: "Ivan"}},
   545  			},
   546  			{
   547  				Type:       "abci.Invoices",
   548  				Attributes: []abci.EventAttribute{{Key: "Number", Value: "1"}},
   549  			},
   550  		}
   551  
   552  		require.NoError(b, s.PublishWithEvents(ctx, "Gamora", events))
   553  	}
   554  }
   555  
   556  // HELPERS
   557  
   558  func assertReceive(t *testing.T, expected interface{}, ch <-chan pubsub.Message, msgAndArgs ...interface{}) {
   559  	select {
   560  	case actual := <-ch:
   561  		require.Equal(t, expected, actual.Data(), msgAndArgs...)
   562  	case <-time.After(1 * time.Second):
   563  		t.Errorf("expected to receive %v from the channel, got nothing after 1s", expected)
   564  		debug.PrintStack()
   565  	}
   566  }
   567  
   568  func assertCanceled(t *testing.T, subscription *pubsub.Subscription, err error) {
   569  	_, ok := <-subscription.Canceled()
   570  	require.False(t, ok)
   571  	require.Equal(t, err, subscription.Err())
   572  }