github.com/number571/tendermint@v0.34.11-gost/libs/pubsub/query/query_test.go (about) 1 package query_test 2 3 import ( 4 "fmt" 5 "strings" 6 "testing" 7 "time" 8 9 abci "github.com/number571/tendermint/abci/types" 10 "github.com/number571/tendermint/libs/pubsub/query" 11 "github.com/stretchr/testify/require" 12 ) 13 14 func expandEvents(flattenedEvents map[string][]string) []abci.Event { 15 events := make([]abci.Event, len(flattenedEvents)) 16 17 for composite, values := range flattenedEvents { 18 tokens := strings.Split(composite, ".") 19 20 attrs := make([]abci.EventAttribute, len(values)) 21 for i, v := range values { 22 attrs[i] = abci.EventAttribute{ 23 Key: tokens[len(tokens)-1], 24 Value: v, 25 } 26 } 27 28 events = append(events, abci.Event{ 29 Type: strings.Join(tokens[:len(tokens)-1], "."), 30 Attributes: attrs, 31 }) 32 } 33 34 return events 35 } 36 37 func TestMatches(t *testing.T) { 38 var ( 39 txDate = "2017-01-01" 40 txTime = "2018-05-03T14:45:00Z" 41 ) 42 43 testCases := []struct { 44 s string 45 events map[string][]string 46 err bool 47 matches bool 48 matchErr bool 49 }{ 50 {"tm.events.type='NewBlock'", map[string][]string{"tm.events.type": {"NewBlock"}}, false, true, false}, 51 {"tx.gas > 7", map[string][]string{"tx.gas": {"8"}}, false, true, false}, 52 {"transfer.amount > 7", map[string][]string{"transfer.amount": {"8stake"}}, false, true, false}, 53 {"transfer.amount > 7", map[string][]string{"transfer.amount": {"8.045stake"}}, false, true, false}, 54 {"transfer.amount > 7.043", map[string][]string{"transfer.amount": {"8.045stake"}}, false, true, false}, 55 {"transfer.amount > 8.045", map[string][]string{"transfer.amount": {"8.045stake"}}, false, false, false}, 56 {"tx.gas > 7 AND tx.gas < 9", map[string][]string{"tx.gas": {"8"}}, false, true, false}, 57 {"body.weight >= 3.5", map[string][]string{"body.weight": {"3.5"}}, false, true, false}, 58 {"account.balance < 1000.0", map[string][]string{"account.balance": {"900"}}, false, true, false}, 59 {"apples.kg <= 4", map[string][]string{"apples.kg": {"4.0"}}, false, true, false}, 60 {"body.weight >= 4.5", map[string][]string{"body.weight": {fmt.Sprintf("%v", float32(4.5))}}, false, true, false}, 61 { 62 "oranges.kg < 4 AND watermellons.kg > 10", 63 map[string][]string{"oranges.kg": {"3"}, "watermellons.kg": {"12"}}, 64 false, 65 true, 66 false, 67 }, 68 {"peaches.kg < 4", map[string][]string{"peaches.kg": {"5"}}, false, false, false}, 69 { 70 "tx.date > DATE 2017-01-01", 71 map[string][]string{"tx.date": {time.Now().Format(query.DateLayout)}}, 72 false, 73 true, 74 false, 75 }, 76 {"tx.date = DATE 2017-01-01", map[string][]string{"tx.date": {txDate}}, false, true, false}, 77 {"tx.date = DATE 2018-01-01", map[string][]string{"tx.date": {txDate}}, false, false, false}, 78 { 79 "tx.time >= TIME 2013-05-03T14:45:00Z", 80 map[string][]string{"tx.time": {time.Now().Format(query.TimeLayout)}}, 81 false, 82 true, 83 false, 84 }, 85 {"tx.time = TIME 2013-05-03T14:45:00Z", map[string][]string{"tx.time": {txTime}}, false, false, false}, 86 {"abci.owner.name CONTAINS 'Igor'", map[string][]string{"abci.owner.name": {"Igor,Ivan"}}, false, true, false}, 87 {"abci.owner.name CONTAINS 'Igor'", map[string][]string{"abci.owner.name": {"Pavel,Ivan"}}, false, false, false}, 88 {"abci.owner.name = 'Igor'", map[string][]string{"abci.owner.name": {"Igor", "Ivan"}}, false, true, false}, 89 { 90 "abci.owner.name = 'Ivan'", 91 map[string][]string{"abci.owner.name": {"Igor", "Ivan"}}, 92 false, 93 true, 94 false, 95 }, 96 { 97 "abci.owner.name = 'Ivan' AND abci.owner.name = 'Igor'", 98 map[string][]string{"abci.owner.name": {"Igor", "Ivan"}}, 99 false, 100 true, 101 false, 102 }, 103 { 104 "abci.owner.name = 'Ivan' AND abci.owner.name = 'John'", 105 map[string][]string{"abci.owner.name": {"Igor", "Ivan"}}, 106 false, 107 false, 108 false, 109 }, 110 { 111 "tm.events.type='NewBlock'", 112 map[string][]string{"tm.events.type": {"NewBlock"}, "app.name": {"fuzzed"}}, 113 false, 114 true, 115 false, 116 }, 117 { 118 "app.name = 'fuzzed'", 119 map[string][]string{"tm.events.type": {"NewBlock"}, "app.name": {"fuzzed"}}, 120 false, 121 true, 122 false, 123 }, 124 { 125 "tm.events.type='NewBlock' AND app.name = 'fuzzed'", 126 map[string][]string{"tm.events.type": {"NewBlock"}, "app.name": {"fuzzed"}}, 127 false, 128 true, 129 false, 130 }, 131 { 132 "tm.events.type='NewHeader' AND app.name = 'fuzzed'", 133 map[string][]string{"tm.events.type": {"NewBlock"}, "app.name": {"fuzzed"}}, 134 false, 135 false, 136 false, 137 }, 138 {"slash EXISTS", 139 map[string][]string{"slash.reason": {"missing_signature"}, "slash.power": {"6000"}}, 140 false, 141 true, 142 false, 143 }, 144 {"sl EXISTS", 145 map[string][]string{"slash.reason": {"missing_signature"}, "slash.power": {"6000"}}, 146 false, 147 true, 148 false, 149 }, 150 {"slash EXISTS", 151 map[string][]string{"transfer.recipient": {"cosmos1gu6y2a0ffteesyeyeesk23082c6998xyzmt9mz"}, 152 "transfer.sender": {"cosmos1crje20aj4gxdtyct7z3knxqry2jqt2fuaey6u5"}}, 153 false, 154 false, 155 false, 156 }, 157 {"slash.reason EXISTS AND slash.power > 1000", 158 map[string][]string{"slash.reason": {"missing_signature"}, "slash.power": {"6000"}}, 159 false, 160 true, 161 false, 162 }, 163 {"slash.reason EXISTS AND slash.power > 1000", 164 map[string][]string{"slash.reason": {"missing_signature"}, "slash.power": {"500"}}, 165 false, 166 false, 167 false, 168 }, 169 {"slash.reason EXISTS", 170 map[string][]string{"transfer.recipient": {"cosmos1gu6y2a0ffteesyeyeesk23082c6998xyzmt9mz"}, 171 "transfer.sender": {"cosmos1crje20aj4gxdtyct7z3knxqry2jqt2fuaey6u5"}}, 172 false, 173 false, 174 false, 175 }, 176 } 177 178 for _, tc := range testCases { 179 q, err := query.New(tc.s) 180 if !tc.err { 181 require.Nil(t, err) 182 } 183 require.NotNil(t, q, "Query '%s' should not be nil", tc.s) 184 185 rawEvents := expandEvents(tc.events) 186 187 if tc.matches { 188 match, err := q.Matches(rawEvents) 189 require.Nil(t, err, "Query '%s' should not error on match %v", tc.s, tc.events) 190 require.True(t, match, "Query '%s' should match %v", tc.s, tc.events) 191 } else { 192 match, err := q.Matches(rawEvents) 193 require.Equal(t, tc.matchErr, err != nil, "Unexpected error for query '%s' match %v", tc.s, tc.events) 194 require.False(t, match, "Query '%s' should not match %v", tc.s, tc.events) 195 } 196 } 197 } 198 199 func TestMustParse(t *testing.T) { 200 require.Panics(t, func() { query.MustParse("=") }) 201 require.NotPanics(t, func() { query.MustParse("tm.events.type='NewBlock'") }) 202 } 203 204 func TestConditions(t *testing.T) { 205 txTime, err := time.Parse(time.RFC3339, "2013-05-03T14:45:00Z") 206 require.NoError(t, err) 207 208 testCases := []struct { 209 s string 210 conditions []query.Condition 211 }{ 212 { 213 s: "tm.events.type='NewBlock'", 214 conditions: []query.Condition{ 215 {CompositeKey: "tm.events.type", Op: query.OpEqual, Operand: "NewBlock"}, 216 }, 217 }, 218 { 219 s: "tx.gas > 7 AND tx.gas < 9", 220 conditions: []query.Condition{ 221 {CompositeKey: "tx.gas", Op: query.OpGreater, Operand: int64(7)}, 222 {CompositeKey: "tx.gas", Op: query.OpLess, Operand: int64(9)}, 223 }, 224 }, 225 { 226 s: "tx.time >= TIME 2013-05-03T14:45:00Z", 227 conditions: []query.Condition{ 228 {CompositeKey: "tx.time", Op: query.OpGreaterEqual, Operand: txTime}, 229 }, 230 }, 231 { 232 s: "slashing EXISTS", 233 conditions: []query.Condition{ 234 {CompositeKey: "slashing", Op: query.OpExists}, 235 }, 236 }, 237 } 238 239 for _, tc := range testCases { 240 q, err := query.New(tc.s) 241 require.Nil(t, err) 242 243 c, err := q.Conditions() 244 require.NoError(t, err) 245 require.Equal(t, tc.conditions, c) 246 } 247 }