You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
6.6 KiB

7 years ago
  1. package kv
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "os"
  6. "testing"
  7. "github.com/stretchr/testify/assert"
  8. "github.com/stretchr/testify/require"
  9. abci "github.com/tendermint/abci/types"
  10. "github.com/tendermint/tendermint/state/txindex"
  11. "github.com/tendermint/tendermint/types"
  12. cmn "github.com/tendermint/tmlibs/common"
  13. db "github.com/tendermint/tmlibs/db"
  14. "github.com/tendermint/tmlibs/pubsub/query"
  15. )
  16. func TestTxIndex(t *testing.T) {
  17. indexer := NewTxIndex(db.NewMemDB())
  18. tx := types.Tx("HELLO WORLD")
  19. txResult := &types.TxResult{Height: 1, Index: 0, Tx: tx, Result: abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeTypeOK, Log: "", Tags: []cmn.KVPair{}, Fee: cmn.KI64Pair{Key: []uint8{}, Value: 0}}}
  20. hash := tx.Hash()
  21. batch := txindex.NewBatch(1)
  22. if err := batch.Add(txResult); err != nil {
  23. t.Error(err)
  24. }
  25. err := indexer.AddBatch(batch)
  26. require.NoError(t, err)
  27. loadedTxResult, err := indexer.Get(hash)
  28. require.NoError(t, err)
  29. assert.Equal(t, txResult, loadedTxResult)
  30. tx2 := types.Tx("BYE BYE WORLD")
  31. txResult2 := &types.TxResult{Height: 1, Index: 0, Tx: tx2, Result: abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeTypeOK, Log: "", Tags: []cmn.KVPair{}, Fee: cmn.KI64Pair{Key: []uint8{}, Value: 0}}}
  32. hash2 := tx2.Hash()
  33. err = indexer.Index(txResult2)
  34. require.NoError(t, err)
  35. loadedTxResult2, err := indexer.Get(hash2)
  36. require.NoError(t, err)
  37. assert.Equal(t, txResult2, loadedTxResult2)
  38. }
  39. func TestTxSearch(t *testing.T) {
  40. allowedTags := []string{"account.number", "account.owner", "account.date"}
  41. indexer := NewTxIndex(db.NewMemDB(), IndexTags(allowedTags))
  42. txResult := txResultWithTags([]cmn.KVPair{
  43. {Key: []byte("account.number"), Value: []byte("1")},
  44. {Key: []byte("account.owner"), Value: []byte("Ivan")},
  45. {Key: []byte("not_allowed"), Value: []byte("Vlad")},
  46. })
  47. hash := txResult.Tx.Hash()
  48. err := indexer.Index(txResult)
  49. require.NoError(t, err)
  50. testCases := []struct {
  51. q string
  52. resultsLength int
  53. }{
  54. // search by hash
  55. {fmt.Sprintf("tx.hash = '%X'", hash), 1},
  56. // search by exact match (one tag)
  57. {"account.number = 1", 1},
  58. // search by exact match (two tags)
  59. {"account.number = 1 AND account.owner = 'Ivan'", 1},
  60. // search by exact match (two tags)
  61. {"account.number = 1 AND account.owner = 'Vlad'", 0},
  62. // search by range
  63. {"account.number >= 1 AND account.number <= 5", 1},
  64. // search by range (lower bound)
  65. {"account.number >= 1", 1},
  66. // search by range (upper bound)
  67. {"account.number <= 5", 1},
  68. // search using not allowed tag
  69. {"not_allowed = 'boom'", 0},
  70. // search for not existing tx result
  71. {"account.number >= 2 AND account.number <= 5", 0},
  72. // search using not existing tag
  73. {"account.date >= TIME 2013-05-03T14:45:00Z", 0},
  74. // search using CONTAINS
  75. {"account.owner CONTAINS 'an'", 1},
  76. // search using CONTAINS
  77. {"account.owner CONTAINS 'Vlad'", 0},
  78. }
  79. for _, tc := range testCases {
  80. t.Run(tc.q, func(t *testing.T) {
  81. results, err := indexer.Search(query.MustParse(tc.q))
  82. assert.NoError(t, err)
  83. assert.Len(t, results, tc.resultsLength)
  84. if tc.resultsLength > 0 {
  85. assert.Equal(t, []*types.TxResult{txResult}, results)
  86. }
  87. })
  88. }
  89. }
  90. func TestTxSearchOneTxWithMultipleSameTagsButDifferentValues(t *testing.T) {
  91. allowedTags := []string{"account.number"}
  92. indexer := NewTxIndex(db.NewMemDB(), IndexTags(allowedTags))
  93. txResult := txResultWithTags([]cmn.KVPair{
  94. {Key: []byte("account.number"), Value: []byte("1")},
  95. {Key: []byte("account.number"), Value: []byte("2")},
  96. })
  97. err := indexer.Index(txResult)
  98. require.NoError(t, err)
  99. results, err := indexer.Search(query.MustParse("account.number >= 1"))
  100. assert.NoError(t, err)
  101. assert.Len(t, results, 1)
  102. assert.Equal(t, []*types.TxResult{txResult}, results)
  103. }
  104. func TestTxSearchMultipleTxs(t *testing.T) {
  105. allowedTags := []string{"account.number"}
  106. indexer := NewTxIndex(db.NewMemDB(), IndexTags(allowedTags))
  107. // indexed first, but bigger height (to test the order of transactions)
  108. txResult := txResultWithTags([]cmn.KVPair{
  109. {Key: []byte("account.number"), Value: []byte("1")},
  110. })
  111. txResult.Tx = types.Tx("Bob's account")
  112. txResult.Height = 2
  113. err := indexer.Index(txResult)
  114. require.NoError(t, err)
  115. // indexed second, but smaller height (to test the order of transactions)
  116. txResult2 := txResultWithTags([]cmn.KVPair{
  117. {Key: []byte("account.number"), Value: []byte("2")},
  118. })
  119. txResult2.Tx = types.Tx("Alice's account")
  120. txResult2.Height = 1
  121. err = indexer.Index(txResult2)
  122. require.NoError(t, err)
  123. results, err := indexer.Search(query.MustParse("account.number >= 1"))
  124. assert.NoError(t, err)
  125. require.Len(t, results, 2)
  126. assert.Equal(t, []*types.TxResult{txResult2, txResult}, results)
  127. }
  128. func TestIndexAllTags(t *testing.T) {
  129. indexer := NewTxIndex(db.NewMemDB(), IndexAllTags())
  130. txResult := txResultWithTags([]cmn.KVPair{
  131. cmn.KVPair{[]byte("account.owner"), []byte("Ivan")},
  132. cmn.KVPair{[]byte("account.number"), []byte("1")},
  133. })
  134. err := indexer.Index(txResult)
  135. require.NoError(t, err)
  136. results, err := indexer.Search(query.MustParse("account.number >= 1"))
  137. assert.NoError(t, err)
  138. assert.Len(t, results, 1)
  139. assert.Equal(t, []*types.TxResult{txResult}, results)
  140. results, err = indexer.Search(query.MustParse("account.owner = 'Ivan'"))
  141. assert.NoError(t, err)
  142. assert.Len(t, results, 1)
  143. assert.Equal(t, []*types.TxResult{txResult}, results)
  144. }
  145. func txResultWithTags(tags []cmn.KVPair) *types.TxResult {
  146. tx := types.Tx("HELLO WORLD")
  147. return &types.TxResult{Height: 1, Index: 0, Tx: tx, Result: abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeTypeOK, Log: "", Tags: tags, Fee: cmn.KI64Pair{Key: []uint8{}, Value: 0}}}
  148. }
  149. func benchmarkTxIndex(txsCount int, b *testing.B) {
  150. tx := types.Tx("HELLO WORLD")
  151. txResult := &types.TxResult{Height: 1, Index: 0, Tx: tx, Result: abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeTypeOK, Log: "", Tags: []cmn.KVPair{}, Fee: cmn.KI64Pair{Key: []uint8{}, Value: 0}}}
  152. dir, err := ioutil.TempDir("", "tx_index_db")
  153. if err != nil {
  154. b.Fatal(err)
  155. }
  156. defer os.RemoveAll(dir) // nolint: errcheck
  157. store := db.NewDB("tx_index", "leveldb", dir)
  158. indexer := NewTxIndex(store)
  159. batch := txindex.NewBatch(txsCount)
  160. for i := 0; i < txsCount; i++ {
  161. if err := batch.Add(txResult); err != nil {
  162. b.Fatal(err)
  163. }
  164. txResult.Index++
  165. }
  166. b.ResetTimer()
  167. for n := 0; n < b.N; n++ {
  168. err = indexer.AddBatch(batch)
  169. }
  170. if err != nil {
  171. b.Fatal(err)
  172. }
  173. }
  174. func BenchmarkTxIndex1(b *testing.B) { benchmarkTxIndex(1, b) }
  175. func BenchmarkTxIndex500(b *testing.B) { benchmarkTxIndex(500, b) }
  176. func BenchmarkTxIndex1000(b *testing.B) { benchmarkTxIndex(1000, b) }
  177. func BenchmarkTxIndex2000(b *testing.B) { benchmarkTxIndex(2000, b) }
  178. func BenchmarkTxIndex10000(b *testing.B) { benchmarkTxIndex(10000, b) }