github.com/KYVENetwork/cometbft/v38@v38.0.3/state/indexer/block/kv/kv_test.go (about)

     1  package kv_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/require"
     9  
    10  	db "github.com/cometbft/cometbft-db"
    11  
    12  	abci "github.com/KYVENetwork/cometbft/v38/abci/types"
    13  	"github.com/KYVENetwork/cometbft/v38/libs/pubsub/query"
    14  	blockidxkv "github.com/KYVENetwork/cometbft/v38/state/indexer/block/kv"
    15  	"github.com/KYVENetwork/cometbft/v38/types"
    16  )
    17  
    18  func TestBlockIndexer(t *testing.T) {
    19  	store := db.NewPrefixDB(db.NewMemDB(), []byte("block_events"))
    20  	indexer := blockidxkv.New(store)
    21  
    22  	require.NoError(t, indexer.Index(types.EventDataNewBlockEvents{
    23  		Height: 1,
    24  		Events: []abci.Event{
    25  			{
    26  				Type: "begin_event",
    27  				Attributes: []abci.EventAttribute{
    28  					{
    29  						Key:   "proposer",
    30  						Value: "FCAA001",
    31  						Index: true,
    32  					},
    33  				},
    34  			},
    35  			{
    36  				Type: "end_event",
    37  				Attributes: []abci.EventAttribute{
    38  					{
    39  						Key:   "foo",
    40  						Value: "100",
    41  						Index: true,
    42  					},
    43  				},
    44  			},
    45  		},
    46  	}))
    47  
    48  	for i := 2; i < 12; i++ {
    49  		var index bool
    50  		if i%2 == 0 {
    51  			index = true
    52  		}
    53  
    54  		require.NoError(t, indexer.Index(types.EventDataNewBlockEvents{
    55  			Height: int64(i),
    56  			Events: []abci.Event{
    57  				{
    58  					Type: "begin_event",
    59  					Attributes: []abci.EventAttribute{
    60  						{
    61  							Key:   "proposer",
    62  							Value: "FCAA001",
    63  							Index: true,
    64  						},
    65  					},
    66  				},
    67  				{
    68  					Type: "end_event",
    69  					Attributes: []abci.EventAttribute{
    70  						{
    71  							Key:   "foo",
    72  							Value: fmt.Sprintf("%d", i),
    73  							Index: index,
    74  						},
    75  					},
    76  				},
    77  			},
    78  		}))
    79  	}
    80  
    81  	testCases := map[string]struct {
    82  		q       *query.Query
    83  		results []int64
    84  	}{
    85  		"block.height = 100": {
    86  			q:       query.MustCompile(`block.height = 100`),
    87  			results: []int64{},
    88  		},
    89  		"block.height = 5": {
    90  			q:       query.MustCompile(`block.height = 5`),
    91  			results: []int64{5},
    92  		},
    93  		"begin_event.key1 = 'value1'": {
    94  			q:       query.MustCompile(`begin_event.key1 = 'value1'`),
    95  			results: []int64{},
    96  		},
    97  		"begin_event.proposer = 'FCAA001'": {
    98  			q:       query.MustCompile(`begin_event.proposer = 'FCAA001'`),
    99  			results: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
   100  		},
   101  		"end_event.foo <= 5": {
   102  			q:       query.MustCompile(`end_event.foo <= 5`),
   103  			results: []int64{2, 4},
   104  		},
   105  		"end_event.foo >= 100": {
   106  			q:       query.MustCompile(`end_event.foo >= 100`),
   107  			results: []int64{1},
   108  		},
   109  		"block.height > 2 AND end_event.foo <= 8": {
   110  			q:       query.MustCompile(`block.height > 2 AND end_event.foo <= 8`),
   111  			results: []int64{4, 6, 8},
   112  		},
   113  		"end_event.foo > 100": {
   114  			q:       query.MustCompile("end_event.foo > 100"),
   115  			results: []int64{},
   116  		},
   117  		"block.height >= 2 AND end_event.foo < 8": {
   118  			q:       query.MustCompile("block.height >= 2 AND end_event.foo < 8"),
   119  			results: []int64{2, 4, 6},
   120  		},
   121  		"begin_event.proposer CONTAINS 'FFFFFFF'": {
   122  			q:       query.MustCompile(`begin_event.proposer CONTAINS 'FFFFFFF'`),
   123  			results: []int64{},
   124  		},
   125  		"begin_event.proposer CONTAINS 'FCAA001'": {
   126  			q:       query.MustCompile(`begin_event.proposer CONTAINS 'FCAA001'`),
   127  			results: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
   128  		},
   129  		"end_event.foo CONTAINS '1'": {
   130  			q:       query.MustCompile("end_event.foo CONTAINS '1'"),
   131  			results: []int64{1, 10},
   132  		},
   133  	}
   134  
   135  	for name, tc := range testCases {
   136  		tc := tc
   137  		t.Run(name, func(t *testing.T) {
   138  			results, err := indexer.Search(context.Background(), tc.q)
   139  			require.NoError(t, err)
   140  			require.Equal(t, tc.results, results)
   141  		})
   142  	}
   143  }
   144  
   145  func TestBlockIndexerMulti(t *testing.T) {
   146  	store := db.NewPrefixDB(db.NewMemDB(), []byte("block_events"))
   147  	indexer := blockidxkv.New(store)
   148  
   149  	require.NoError(t, indexer.Index(types.EventDataNewBlockEvents{
   150  		Height: 1,
   151  		Events: []abci.Event{
   152  			{},
   153  			{
   154  				Type: "end_event",
   155  				Attributes: []abci.EventAttribute{
   156  					{
   157  						Key:   "foo",
   158  						Value: "100",
   159  						Index: true,
   160  					},
   161  					{
   162  						Key:   "bar",
   163  						Value: "200",
   164  						Index: true,
   165  					},
   166  				},
   167  			},
   168  			{
   169  				Type: "end_event",
   170  				Attributes: []abci.EventAttribute{
   171  					{
   172  						Key:   "foo",
   173  						Value: "300",
   174  						Index: true,
   175  					},
   176  					{
   177  						Key:   "bar",
   178  						Value: "500",
   179  						Index: true,
   180  					},
   181  				},
   182  			},
   183  		},
   184  	}))
   185  
   186  	require.NoError(t, indexer.Index(types.EventDataNewBlockEvents{
   187  		Height: 2,
   188  		Events: []abci.Event{
   189  			{},
   190  			{
   191  				Type: "end_event",
   192  				Attributes: []abci.EventAttribute{
   193  					{
   194  						Key:   "foo",
   195  						Value: "100",
   196  						Index: true,
   197  					},
   198  					{
   199  						Key:   "bar",
   200  						Value: "200",
   201  						Index: true,
   202  					},
   203  				},
   204  			},
   205  			{
   206  				Type: "end_event",
   207  				Attributes: []abci.EventAttribute{
   208  					{
   209  						Key:   "foo",
   210  						Value: "300",
   211  						Index: true,
   212  					},
   213  					{
   214  						Key:   "bar",
   215  						Value: "400",
   216  						Index: true,
   217  					},
   218  				},
   219  			},
   220  		},
   221  	}))
   222  
   223  	testCases := map[string]struct {
   224  		q       *query.Query
   225  		results []int64
   226  	}{
   227  
   228  		"query return all events from a height - exact": {
   229  			q:       query.MustCompile("block.height = 1"),
   230  			results: []int64{1},
   231  		},
   232  		"query return all events from a height - exact (deduplicate height)": {
   233  			q:       query.MustCompile("block.height = 1 AND block.height = 2"),
   234  			results: []int64{1},
   235  		},
   236  		"query return all events from a height - range": {
   237  			q:       query.MustCompile("block.height < 2 AND block.height > 0 AND block.height > 0"),
   238  			results: []int64{1},
   239  		},
   240  		"query return all events from a height - range 2": {
   241  			q:       query.MustCompile("block.height < 3 AND block.height < 2 AND block.height > 0 AND block.height > 0"),
   242  			results: []int64{1},
   243  		},
   244  		"query return all events from a height - range 3": {
   245  			q:       query.MustCompile("block.height < 1 AND block.height > 1"),
   246  			results: []int64{},
   247  		},
   248  		"query matches fields from same event": {
   249  			q:       query.MustCompile("end_event.bar < 300 AND end_event.foo = 100 AND block.height > 0 AND block.height <= 2"),
   250  			results: []int64{1, 2},
   251  		},
   252  		"query matches fields from multiple events": {
   253  			q:       query.MustCompile("end_event.foo = 100 AND end_event.bar = 400 AND block.height = 2"),
   254  			results: []int64{},
   255  		},
   256  		"query matches fields from multiple events 2": {
   257  			q:       query.MustCompile("end_event.foo = 100 AND end_event.bar > 200 AND block.height > 0 AND block.height < 3"),
   258  			results: []int64{},
   259  		},
   260  		"query matches fields from multiple events allowed": {
   261  			q:       query.MustCompile("end_event.foo = 100 AND end_event.bar = 400"),
   262  			results: []int64{},
   263  		},
   264  		"query matches fields from all events whose attribute is within range": {
   265  			q:       query.MustCompile("block.height  = 2 AND end_event.foo < 300"),
   266  			results: []int64{2},
   267  		},
   268  		"match attributes across events with height constraint": {
   269  			q:       query.MustCompile("end_event.foo = 100 AND end_event.bar = 400 AND block.height = 2"),
   270  			results: []int64{},
   271  		},
   272  		"query using CONTAINS matches fields from all events whose attribute is within range": {
   273  			q:       query.MustCompile("block.height  = 2 AND end_event.foo CONTAINS '30'"),
   274  			results: []int64{2},
   275  		},
   276  		"query matches all fields from multiple events": {
   277  			q:       query.MustCompile("end_event.bar > 100 AND end_event.bar <= 500"),
   278  			results: []int64{1, 2},
   279  		},
   280  		"query with height range and height equality - should ignore equality": {
   281  			q:       query.MustCompile("block.height = 2 AND end_event.foo >= 100 AND block.height < 2"),
   282  			results: []int64{1},
   283  		},
   284  		"query with non-existent field": {
   285  			q:       query.MustCompile("end_event.baz = 100"),
   286  			results: []int64{},
   287  		},
   288  		"query with non-existent field ": {
   289  			q:       query.MustCompile("end_event.baz = 100"),
   290  			results: []int64{},
   291  		},
   292  	}
   293  
   294  	for name, tc := range testCases {
   295  		tc := tc
   296  		t.Run(name, func(t *testing.T) {
   297  			results, err := indexer.Search(context.Background(), tc.q)
   298  			require.NoError(t, err)
   299  			require.Equal(t, tc.results, results)
   300  		})
   301  	}
   302  }
   303  
   304  func TestBigInt(t *testing.T) {
   305  
   306  	bigInt := "10000000000000000000"
   307  	bigFloat := bigInt + ".76"
   308  	bigFloatLower := bigInt + ".1"
   309  	bigIntSmaller := "9999999999999999999"
   310  	store := db.NewPrefixDB(db.NewMemDB(), []byte("block_events"))
   311  	indexer := blockidxkv.New(store)
   312  
   313  	require.NoError(t, indexer.Index(types.EventDataNewBlockEvents{
   314  		Height: 1,
   315  		Events: []abci.Event{
   316  			{},
   317  			{
   318  				Type: "end_event",
   319  				Attributes: []abci.EventAttribute{
   320  					{
   321  						Key:   "foo",
   322  						Value: "100",
   323  						Index: true,
   324  					},
   325  					{
   326  						Key:   "bar",
   327  						Value: bigFloat,
   328  						Index: true,
   329  					},
   330  					{
   331  						Key:   "bar_lower",
   332  						Value: bigFloatLower,
   333  						Index: true,
   334  					},
   335  				},
   336  			},
   337  			{
   338  				Type: "end_event",
   339  				Attributes: []abci.EventAttribute{
   340  					{
   341  						Key:   "foo",
   342  						Value: bigInt,
   343  						Index: true,
   344  					},
   345  					{
   346  						Key:   "bar",
   347  						Value: "500",
   348  						Index: true,
   349  					},
   350  					{
   351  						Key:   "bla",
   352  						Value: "500.5",
   353  						Index: true,
   354  					},
   355  				},
   356  			},
   357  		},
   358  	},
   359  	))
   360  
   361  	testCases := map[string]struct {
   362  		q       *query.Query
   363  		results []int64
   364  	}{
   365  
   366  		"query return all events from a height - exact": {
   367  			q:       query.MustCompile("block.height = 1"),
   368  			results: []int64{1},
   369  		},
   370  		"query return all events from a height - exact (deduplicate height)": {
   371  			q:       query.MustCompile("block.height = 1 AND block.height = 2"),
   372  			results: []int64{1},
   373  		},
   374  		"query return all events from a height - range": {
   375  			q:       query.MustCompile("block.height < 2 AND block.height > 0 AND block.height > 0"),
   376  			results: []int64{1},
   377  		},
   378  		"query matches fields with big int and height - no match": {
   379  			q:       query.MustCompile("end_event.foo = " + bigInt + " AND end_event.bar = 500 AND block.height = 2"),
   380  			results: []int64{},
   381  		},
   382  		"query matches fields with big int with less and height - no match": {
   383  			q:       query.MustCompile("end_event.foo <= " + bigInt + " AND end_event.bar = 500 AND block.height = 2"),
   384  			results: []int64{},
   385  		},
   386  		"query matches fields with big int and height - match": {
   387  			q:       query.MustCompile("end_event.foo = " + bigInt + " AND end_event.bar = 500 AND block.height = 1"),
   388  			results: []int64{1},
   389  		},
   390  		"query matches big int in range": {
   391  			q:       query.MustCompile("end_event.foo = " + bigInt),
   392  			results: []int64{1},
   393  		},
   394  		"query matches big int in range with float with equality ": {
   395  			q:       query.MustCompile("end_event.bar >= " + bigInt),
   396  			results: []int64{1},
   397  		},
   398  		"query matches big int in range with float ": {
   399  			q:       query.MustCompile("end_event.bar > " + bigInt),
   400  			results: []int64{1},
   401  		},
   402  		"query matches big int in range with float lower dec point ": {
   403  			q:       query.MustCompile("end_event.bar_lower > " + bigInt),
   404  			results: []int64{1},
   405  		},
   406  		"query matches big int in range with float with less - found": {
   407  			q:       query.MustCompile("end_event.foo <= " + bigInt),
   408  			results: []int64{1},
   409  		},
   410  		"query matches big int in range with float with less with height range - found": {
   411  			q:       query.MustCompile("end_event.foo <= " + bigInt + " AND block.height > 0"),
   412  			results: []int64{1},
   413  		},
   414  		"query matches big int in range with float with less - not found": {
   415  			q:       query.MustCompile("end_event.foo < " + bigInt + " AND end_event.foo > 100"),
   416  			results: []int64{},
   417  		},
   418  		"query does not parse float": {
   419  			q:       query.MustCompile("end_event.bla >= 500"),
   420  			results: []int64{1},
   421  		},
   422  		"query condition float": {
   423  			q:       query.MustCompile("end_event.bla < " + bigFloat),
   424  			results: []int64{1},
   425  		},
   426  		"query condition big int plus one": {
   427  			q:       query.MustCompile("end_event.foo > " + bigIntSmaller),
   428  			results: []int64{1},
   429  		},
   430  	}
   431  	for name, tc := range testCases {
   432  		tc := tc
   433  		t.Run(name, func(t *testing.T) {
   434  			results, err := indexer.Search(context.Background(), tc.q)
   435  			require.NoError(t, err)
   436  			require.Equal(t, tc.results, results)
   437  		})
   438  	}
   439  }