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 }