bitbucket.org/number571/tendermint@v0.8.14/state/indexer/sink/kv/kv_test.go (about)

     1  package kv
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	abci "bitbucket.org/number571/tendermint/abci/types"
     9  	"bitbucket.org/number571/tendermint/libs/pubsub/query"
    10  	"bitbucket.org/number571/tendermint/state/indexer"
    11  	kvtx "bitbucket.org/number571/tendermint/state/indexer/tx/kv"
    12  	"bitbucket.org/number571/tendermint/types"
    13  	"github.com/gogo/protobuf/proto"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  	db "github.com/tendermint/tm-db"
    17  )
    18  
    19  func TestType(t *testing.T) {
    20  	kvSink := NewEventSink(db.NewMemDB())
    21  	assert.Equal(t, indexer.KV, kvSink.Type())
    22  }
    23  
    24  func TestStop(t *testing.T) {
    25  	kvSink := NewEventSink(db.NewMemDB())
    26  	assert.Nil(t, kvSink.Stop())
    27  }
    28  
    29  func TestBlockFuncs(t *testing.T) {
    30  	store := db.NewPrefixDB(db.NewMemDB(), []byte("block_events"))
    31  	indexer := NewEventSink(store)
    32  
    33  	require.NoError(t, indexer.IndexBlockEvents(types.EventDataNewBlockHeader{
    34  		Header: types.Header{Height: 1},
    35  		ResultBeginBlock: abci.ResponseBeginBlock{
    36  			Events: []abci.Event{
    37  				{
    38  					Type: "begin_event",
    39  					Attributes: []abci.EventAttribute{
    40  						{
    41  							Key:   "proposer",
    42  							Value: "FCAA001",
    43  							Index: true,
    44  						},
    45  					},
    46  				},
    47  			},
    48  		},
    49  		ResultEndBlock: abci.ResponseEndBlock{
    50  			Events: []abci.Event{
    51  				{
    52  					Type: "end_event",
    53  					Attributes: []abci.EventAttribute{
    54  						{
    55  							Key:   "foo",
    56  							Value: "100",
    57  							Index: true,
    58  						},
    59  					},
    60  				},
    61  			},
    62  		},
    63  	}))
    64  
    65  	b, e := indexer.HasBlock(1)
    66  	assert.Nil(t, e)
    67  	assert.True(t, b)
    68  
    69  	for i := 2; i < 12; i++ {
    70  		var index bool
    71  		if i%2 == 0 {
    72  			index = true
    73  		}
    74  
    75  		require.NoError(t, indexer.IndexBlockEvents(types.EventDataNewBlockHeader{
    76  			Header: types.Header{Height: int64(i)},
    77  			ResultBeginBlock: abci.ResponseBeginBlock{
    78  				Events: []abci.Event{
    79  					{
    80  						Type: "begin_event",
    81  						Attributes: []abci.EventAttribute{
    82  							{
    83  								Key:   "proposer",
    84  								Value: "FCAA001",
    85  								Index: true,
    86  							},
    87  						},
    88  					},
    89  				},
    90  			},
    91  			ResultEndBlock: abci.ResponseEndBlock{
    92  				Events: []abci.Event{
    93  					{
    94  						Type: "end_event",
    95  						Attributes: []abci.EventAttribute{
    96  							{
    97  								Key:   "foo",
    98  								Value: fmt.Sprintf("%d", i),
    99  								Index: index,
   100  							},
   101  						},
   102  					},
   103  				},
   104  			},
   105  		}))
   106  	}
   107  
   108  	testCases := map[string]struct {
   109  		q       *query.Query
   110  		results []int64
   111  	}{
   112  		"block.height = 100": {
   113  			q:       query.MustParse("block.height = 100"),
   114  			results: []int64{},
   115  		},
   116  		"block.height = 5": {
   117  			q:       query.MustParse("block.height = 5"),
   118  			results: []int64{5},
   119  		},
   120  		"begin_event.key1 = 'value1'": {
   121  			q:       query.MustParse("begin_event.key1 = 'value1'"),
   122  			results: []int64{},
   123  		},
   124  		"begin_event.proposer = 'FCAA001'": {
   125  			q:       query.MustParse("begin_event.proposer = 'FCAA001'"),
   126  			results: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
   127  		},
   128  		"end_event.foo <= 5": {
   129  			q:       query.MustParse("end_event.foo <= 5"),
   130  			results: []int64{2, 4},
   131  		},
   132  		"end_event.foo >= 100": {
   133  			q:       query.MustParse("end_event.foo >= 100"),
   134  			results: []int64{1},
   135  		},
   136  		"block.height > 2 AND end_event.foo <= 8": {
   137  			q:       query.MustParse("block.height > 2 AND end_event.foo <= 8"),
   138  			results: []int64{4, 6, 8},
   139  		},
   140  		"begin_event.proposer CONTAINS 'FFFFFFF'": {
   141  			q:       query.MustParse("begin_event.proposer CONTAINS 'FFFFFFF'"),
   142  			results: []int64{},
   143  		},
   144  		"begin_event.proposer CONTAINS 'FCAA001'": {
   145  			q:       query.MustParse("begin_event.proposer CONTAINS 'FCAA001'"),
   146  			results: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
   147  		},
   148  	}
   149  
   150  	for name, tc := range testCases {
   151  		tc := tc
   152  		t.Run(name, func(t *testing.T) {
   153  			results, err := indexer.SearchBlockEvents(context.Background(), tc.q)
   154  			require.NoError(t, err)
   155  			require.Equal(t, tc.results, results)
   156  		})
   157  	}
   158  }
   159  
   160  func TestTxSearchWithCancelation(t *testing.T) {
   161  	indexer := NewEventSink(db.NewMemDB())
   162  
   163  	txResult := txResultWithEvents([]abci.Event{
   164  		{Type: "account", Attributes: []abci.EventAttribute{{Key: "number", Value: "1", Index: true}}},
   165  		{Type: "account", Attributes: []abci.EventAttribute{{Key: "owner", Value: "Ivan", Index: true}}},
   166  		{Type: "", Attributes: []abci.EventAttribute{{Key: "not_allowed", Value: "Vlad", Index: true}}},
   167  	})
   168  	err := indexer.IndexTxEvents([]*abci.TxResult{txResult})
   169  	require.NoError(t, err)
   170  
   171  	r, e := indexer.GetTxByHash(types.Tx("HELLO WORLD").Hash())
   172  	assert.Nil(t, e)
   173  	assert.Equal(t, r, txResult)
   174  
   175  	ctx, cancel := context.WithCancel(context.Background())
   176  	cancel()
   177  	results, err := indexer.SearchTxEvents(ctx, query.MustParse("account.number = 1"))
   178  	assert.NoError(t, err)
   179  	assert.Empty(t, results)
   180  }
   181  
   182  func TestTxSearchDeprecatedIndexing(t *testing.T) {
   183  	esdb := db.NewMemDB()
   184  	indexer := NewEventSink(esdb)
   185  
   186  	// index tx using events indexing (composite key)
   187  	txResult1 := txResultWithEvents([]abci.Event{
   188  		{Type: "account", Attributes: []abci.EventAttribute{{Key: "number", Value: "1", Index: true}}},
   189  	})
   190  	hash1 := types.Tx(txResult1.Tx).Hash()
   191  
   192  	err := indexer.IndexTxEvents([]*abci.TxResult{txResult1})
   193  	require.NoError(t, err)
   194  
   195  	// index tx also using deprecated indexing (event as key)
   196  	txResult2 := txResultWithEvents(nil)
   197  	txResult2.Tx = types.Tx("HELLO WORLD 2")
   198  
   199  	hash2 := types.Tx(txResult2.Tx).Hash()
   200  	b := esdb.NewBatch()
   201  
   202  	rawBytes, err := proto.Marshal(txResult2)
   203  	require.NoError(t, err)
   204  
   205  	depKey := []byte(fmt.Sprintf("%s/%s/%d/%d",
   206  		"sender",
   207  		"addr1",
   208  		txResult2.Height,
   209  		txResult2.Index,
   210  	))
   211  
   212  	err = b.Set(depKey, hash2)
   213  	require.NoError(t, err)
   214  	err = b.Set(kvtx.KeyFromHeight(txResult2), hash2)
   215  	require.NoError(t, err)
   216  	err = b.Set(hash2, rawBytes)
   217  	require.NoError(t, err)
   218  	err = b.Write()
   219  	require.NoError(t, err)
   220  
   221  	testCases := []struct {
   222  		q       string
   223  		results []*abci.TxResult
   224  	}{
   225  		// search by hash
   226  		{fmt.Sprintf("tx.hash = '%X'", hash1), []*abci.TxResult{txResult1}},
   227  		// search by hash
   228  		{fmt.Sprintf("tx.hash = '%X'", hash2), []*abci.TxResult{txResult2}},
   229  		// search by exact match (one key)
   230  		{"account.number = 1", []*abci.TxResult{txResult1}},
   231  		{"account.number >= 1 AND account.number <= 5", []*abci.TxResult{txResult1}},
   232  		// search by range (lower bound)
   233  		{"account.number >= 1", []*abci.TxResult{txResult1}},
   234  		// search by range (upper bound)
   235  		{"account.number <= 5", []*abci.TxResult{txResult1}},
   236  		// search using not allowed key
   237  		{"not_allowed = 'boom'", []*abci.TxResult{}},
   238  		// search for not existing tx result
   239  		{"account.number >= 2 AND account.number <= 5", []*abci.TxResult{}},
   240  		// search using not existing key
   241  		{"account.date >= TIME 2013-05-03T14:45:00Z", []*abci.TxResult{}},
   242  		// search by deprecated key
   243  		{"sender = 'addr1'", []*abci.TxResult{txResult2}},
   244  	}
   245  
   246  	ctx := context.Background()
   247  
   248  	for _, tc := range testCases {
   249  		tc := tc
   250  		t.Run(tc.q, func(t *testing.T) {
   251  			results, err := indexer.SearchTxEvents(ctx, query.MustParse(tc.q))
   252  			require.NoError(t, err)
   253  			for _, txr := range results {
   254  				for _, tr := range tc.results {
   255  					assert.True(t, proto.Equal(tr, txr))
   256  				}
   257  			}
   258  		})
   259  	}
   260  }
   261  
   262  func TestTxSearchOneTxWithMultipleSameTagsButDifferentValues(t *testing.T) {
   263  	indexer := NewEventSink(db.NewMemDB())
   264  
   265  	txResult := txResultWithEvents([]abci.Event{
   266  		{Type: "account", Attributes: []abci.EventAttribute{{Key: "number", Value: "1", Index: true}}},
   267  		{Type: "account", Attributes: []abci.EventAttribute{{Key: "number", Value: "2", Index: true}}},
   268  	})
   269  
   270  	err := indexer.IndexTxEvents([]*abci.TxResult{txResult})
   271  	require.NoError(t, err)
   272  
   273  	ctx := context.Background()
   274  
   275  	results, err := indexer.SearchTxEvents(ctx, query.MustParse("account.number >= 1"))
   276  	assert.NoError(t, err)
   277  
   278  	assert.Len(t, results, 1)
   279  	for _, txr := range results {
   280  		assert.True(t, proto.Equal(txResult, txr))
   281  	}
   282  }
   283  
   284  func TestTxSearchMultipleTxs(t *testing.T) {
   285  	indexer := NewEventSink(db.NewMemDB())
   286  
   287  	// indexed first, but bigger height (to test the order of transactions)
   288  	txResult := txResultWithEvents([]abci.Event{
   289  		{Type: "account", Attributes: []abci.EventAttribute{{Key: "number", Value: "1", Index: true}}},
   290  	})
   291  
   292  	txResult.Tx = types.Tx("Bob's account")
   293  	txResult.Height = 2
   294  	txResult.Index = 1
   295  	err := indexer.IndexTxEvents([]*abci.TxResult{txResult})
   296  	require.NoError(t, err)
   297  
   298  	// indexed second, but smaller height (to test the order of transactions)
   299  	txResult2 := txResultWithEvents([]abci.Event{
   300  		{Type: "account", Attributes: []abci.EventAttribute{{Key: "number", Value: "2", Index: true}}},
   301  	})
   302  	txResult2.Tx = types.Tx("Alice's account")
   303  	txResult2.Height = 1
   304  	txResult2.Index = 2
   305  
   306  	err = indexer.IndexTxEvents([]*abci.TxResult{txResult2})
   307  	require.NoError(t, err)
   308  
   309  	// indexed third (to test the order of transactions)
   310  	txResult3 := txResultWithEvents([]abci.Event{
   311  		{Type: "account", Attributes: []abci.EventAttribute{{Key: "number", Value: "3", Index: true}}},
   312  	})
   313  	txResult3.Tx = types.Tx("Jack's account")
   314  	txResult3.Height = 1
   315  	txResult3.Index = 1
   316  	err = indexer.IndexTxEvents([]*abci.TxResult{txResult3})
   317  	require.NoError(t, err)
   318  
   319  	// indexed fourth (to test we don't include txs with similar events)
   320  	// https://bitbucket.org/number571/tendermint/issues/2908
   321  	txResult4 := txResultWithEvents([]abci.Event{
   322  		{Type: "account", Attributes: []abci.EventAttribute{{Key: "number.id", Value: "1", Index: true}}},
   323  	})
   324  	txResult4.Tx = types.Tx("Mike's account")
   325  	txResult4.Height = 2
   326  	txResult4.Index = 2
   327  	err = indexer.IndexTxEvents([]*abci.TxResult{txResult4})
   328  	require.NoError(t, err)
   329  
   330  	ctx := context.Background()
   331  
   332  	results, err := indexer.SearchTxEvents(ctx, query.MustParse("account.number >= 1"))
   333  	assert.NoError(t, err)
   334  
   335  	require.Len(t, results, 3)
   336  }
   337  
   338  func txResultWithEvents(events []abci.Event) *abci.TxResult {
   339  	tx := types.Tx("HELLO WORLD")
   340  	return &abci.TxResult{
   341  		Height: 1,
   342  		Index:  0,
   343  		Tx:     tx,
   344  		Result: abci.ResponseDeliverTx{
   345  			Data:   []byte{0},
   346  			Code:   abci.CodeTypeOK,
   347  			Log:    "",
   348  			Events: events,
   349  		},
   350  	}
   351  }