github.com/willyham/dosa@v2.3.1-0.20171024181418-1e446d37ee71+incompatible/range_conditions_test.go (about) 1 // Copyright (c) 2017 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package dosa_test 22 23 import ( 24 "testing" 25 26 "time" 27 28 "github.com/stretchr/testify/assert" 29 "github.com/uber-go/dosa" 30 ) 31 32 // pk = ((a, b), c, d, e) 33 var testEntityRange = &dosa.EntityDefinition{ 34 Name: "testentityrange", 35 Key: &dosa.PrimaryKey{ 36 PartitionKeys: []string{"a", "b"}, 37 ClusteringKeys: []*dosa.ClusteringKey{ 38 { 39 Name: "c", 40 Descending: true, 41 }, 42 { 43 Name: "d", 44 Descending: true, 45 }, 46 { 47 Name: "e", 48 Descending: true, 49 }, 50 }, 51 }, 52 Columns: []*dosa.ColumnDefinition{ 53 { 54 Name: "a", 55 Type: dosa.TUUID, 56 }, 57 58 { 59 Name: "c", 60 Type: dosa.Int32, 61 }, 62 { 63 Name: "e", 64 Type: dosa.String, 65 }, 66 { 67 Name: "d", 68 Type: dosa.Timestamp, 69 }, 70 { 71 Name: "b", 72 Type: dosa.Int64, 73 }, 74 { 75 Name: "f", 76 Type: dosa.Blob, 77 }, 78 }, 79 } 80 81 var columnToFieldMap = map[string]string{ 82 "a": "FieldA", 83 "b": "FieldB", 84 "c": "FieldC", 85 "d": "FieldD", 86 "e": "FieldE", 87 "f": "FieldF", 88 } 89 90 var simpleTransformer = func(x string) string { 91 return columnToFieldMap[x] 92 } 93 94 func TestEnsureValidRangeConditions(t *testing.T) { 95 assert.NoError(t, testEntityRange.EnsureValid()) // sanity check 96 97 type validCase struct { 98 conds map[string][]*dosa.Condition 99 desc string 100 } 101 validCases := []validCase{ 102 { 103 conds: map[string][]*dosa.Condition{ 104 "a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}}, 105 "b": {{dosa.Eq, int64(100)}}, 106 }, 107 desc: "supply only partition keys is allowed, no conditions on clustering keys", 108 }, 109 { 110 conds: map[string][]*dosa.Condition{ 111 "a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}}, 112 "b": {{dosa.Eq, int64(100)}}, 113 "c": {{dosa.Eq, int32(99)}}, 114 }, 115 desc: "eq condition on first clustering key, no condition on second and third", 116 }, 117 { 118 conds: map[string][]*dosa.Condition{ 119 "a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}}, 120 "b": {{dosa.Eq, int64(100)}}, 121 "c": {{dosa.GtOrEq, int32(99)}, {dosa.Lt, int32(200)}}, 122 }, 123 desc: "close range condition on first clustering key, no condition on second and third", 124 }, 125 { 126 conds: map[string][]*dosa.Condition{ 127 "a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}}, 128 "b": {{dosa.Eq, int64(100)}}, 129 "c": {{dosa.Eq, int32(99)}}, 130 "d": {{dosa.LtOrEq, time.Now()}}, 131 }, 132 desc: "open range condition on second clustering key, no restaint on third", 133 }, 134 { 135 conds: map[string][]*dosa.Condition{ 136 "a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}}, 137 "b": {{dosa.Eq, int64(100)}}, 138 "c": {{dosa.Eq, int32(99)}}, 139 "d": {{dosa.Eq, time.Unix(100, 0)}}, 140 }, 141 desc: "eq condition on second clustering key, no restaint on third", 142 }, 143 { 144 conds: map[string][]*dosa.Condition{ 145 "a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}}, 146 "b": {{dosa.Eq, int64(100)}}, 147 "c": {{dosa.Eq, int32(99)}}, 148 "d": {{dosa.Eq, time.Unix(100, 0)}}, 149 "e": {{dosa.Gt, "aaa"}, {dosa.Lt, "zzz"}}, 150 }, 151 desc: "close range condition on third/last clustering key with < and >", 152 }, 153 { 154 conds: map[string][]*dosa.Condition{ 155 "a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}}, 156 "b": {{dosa.Eq, int64(100)}}, 157 "c": {{dosa.Eq, int32(99)}}, 158 "d": {{dosa.Eq, time.Unix(100, 0)}}, 159 "e": {{dosa.GtOrEq, "aaa"}, {dosa.LtOrEq, "zzz"}}, 160 }, 161 desc: "close range condition on third/last clustering key with <= and >=", 162 }, 163 { 164 conds: map[string][]*dosa.Condition{ 165 "a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}}, 166 "b": {{dosa.Eq, int64(100)}}, 167 "c": {{dosa.Eq, int32(99)}}, 168 "d": {{dosa.Eq, time.Unix(100, 0)}}, 169 "e": {{dosa.GtOrEq, "aaa"}, {dosa.Lt, "zzz"}}, 170 }, 171 desc: "close range condition on third/last clustering key with < and >=", 172 }, 173 { 174 conds: map[string][]*dosa.Condition{ 175 "a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}}, 176 "b": {{dosa.Eq, int64(100)}}, 177 "c": {{dosa.Eq, int32(99)}}, 178 "d": {{dosa.Eq, time.Unix(100, 0)}}, 179 "e": {{dosa.Gt, "aaa"}, {dosa.LtOrEq, "zzz"}}, 180 }, 181 desc: "close range condition on third/last clustering key with <= and >", 182 }, 183 } 184 185 type invalidCase struct { 186 conds map[string][]*dosa.Condition 187 desc string 188 errMsg string 189 errField string 190 } 191 192 invalidCases := []invalidCase{ 193 { 194 conds: map[string][]*dosa.Condition{ 195 "a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}}, 196 "b": {{dosa.Eq, int64(100)}}, 197 "f": {{dosa.Eq, []byte{1, 2, 3}}}, 198 }, 199 errMsg: "cannot enforce condition on non-key column", 200 desc: "conditions on non-key column", 201 errField: columnToFieldMap["f"], 202 }, 203 { 204 conds: map[string][]*dosa.Condition{ 205 "a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}}, 206 "c": {{dosa.Gt, int32(100)}}, 207 }, 208 errMsg: "missing Eq condition on partition keys", 209 desc: "missing partition key condition", 210 errField: columnToFieldMap["b"], 211 }, 212 { 213 conds: map[string][]*dosa.Condition{ 214 "a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}}, 215 "b": {{dosa.Gt, int64(100)}}, 216 }, 217 errMsg: "invalid conditions for partition key", 218 desc: "Gt condition on partition key", 219 errField: columnToFieldMap["b"], 220 }, 221 { 222 conds: map[string][]*dosa.Condition{ 223 "a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}}, 224 "b": {{dosa.Eq, int64(100)}, {dosa.Eq, int64(200)}}, 225 }, 226 errMsg: "invalid conditions for partition key", 227 desc: "more than one conditions on partition key", 228 errField: columnToFieldMap["b"], 229 }, 230 { 231 conds: map[string][]*dosa.Condition{ 232 "a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}}, 233 "b": {{dosa.Eq, "100"}}, 234 }, 235 errMsg: "does not have expected type", 236 desc: "wrong value type for partition key", 237 errField: columnToFieldMap["b"], 238 }, 239 { 240 conds: map[string][]*dosa.Condition{ 241 "a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}}, 242 "b": {{dosa.Eq, int64(100)}}, 243 "c": {{dosa.Eq, "100"}}, 244 }, 245 errMsg: "invalid value for", 246 desc: "wrong value type for clustering key", 247 errField: columnToFieldMap["c"], 248 }, 249 { 250 conds: map[string][]*dosa.Condition{ 251 "a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}}, 252 "b": {{dosa.Eq, int64(100)}}, 253 "c": {{dosa.Lt, int32(100)}, {dosa.Gt, int32(200)}}, 254 }, 255 errMsg: "invalid or unsupported conditions for clustering key", 256 desc: "invalid range condition on clustering key", 257 errField: columnToFieldMap["c"], 258 }, 259 { 260 conds: map[string][]*dosa.Condition{ 261 "a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}}, 262 "b": {{dosa.Eq, int64(100)}}, 263 "c": {{dosa.Lt, int32(100)}, {dosa.Gt, int32(200)}}, 264 "d": {{dosa.GtOrEq, time.Now()}}, 265 }, 266 errMsg: "exact one Eq condition can be applied except for the last", 267 desc: "applying conditions other than eq to clustering keys that's not the last retrained", 268 errField: columnToFieldMap["c"], 269 }, 270 { 271 conds: map[string][]*dosa.Condition{ 272 "a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}}, 273 "b": {{dosa.Eq, int64(100)}}, 274 "c": {{dosa.Lt, int32(100)}, {dosa.Gt, int32(200)}}, 275 "d": {{dosa.Eq, time.Unix(100, 0)}}, 276 "e": {{dosa.Eq, "aaa"}}, 277 }, 278 errMsg: "exact one Eq condition can be applied except for the last", 279 desc: "applying conditions other than eq to clustering keys that's not the last retrained, " + 280 "different last retained clusterin key", 281 errField: columnToFieldMap["c"], 282 }, 283 { 284 conds: map[string][]*dosa.Condition{ 285 "a": {{dosa.Eq, dosa.UUID("66DF78EB-C41D-48EF-B366-0C7F91C5CE43")}}, 286 "b": {{dosa.Eq, int64(100)}}, 287 "c": {{dosa.Eq, int32(100)}}, 288 "e": {{dosa.Eq, "aaa"}}, 289 }, 290 errMsg: "conditions must be applied consecutively on clustering keys", 291 desc: "applying conditions non-consecutively to clustering keys", 292 errField: columnToFieldMap["e"], 293 }, 294 } 295 296 for _, c := range validCases { 297 assert.NoError(t, dosa.EnsureValidRangeConditions(testEntityRange, testEntityRange.Key, c.conds, simpleTransformer), c.desc) 298 } 299 300 for _, c := range invalidCases { 301 err := dosa.EnsureValidRangeConditions(testEntityRange, testEntityRange.Key, c.conds, simpleTransformer) 302 if assert.Error(t, err, c.desc) { 303 assert.Contains(t, err.Error(), c.errMsg, c.desc) 304 assert.Contains(t, err.Error(), c.errField, c.desc) 305 } 306 } 307 }