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 }