github.com/willyham/dosa@v2.3.1-0.20171024181418-1e446d37ee71+incompatible/entity_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 "github.com/stretchr/testify/assert" 27 "github.com/uber-go/dosa" 28 ) 29 30 func TestEntityDefinitionEnsureValid(t *testing.T) { 31 type testData struct { 32 e *dosa.EntityDefinition 33 valid bool 34 msg string 35 } 36 37 invalidName := getValidEntityDefinition() 38 invalidName.Name = "foo=bar" 39 40 nilColumn := getValidEntityDefinition() 41 nilColumn.Columns = append(nilColumn.Columns, nil) 42 43 invalidColumnName := getValidEntityDefinition() 44 invalidColumnName.Columns[0].Name = "AAA" 45 46 dupColumnNames := getValidEntityDefinition() 47 dupColumnNames.Columns = append(dupColumnNames.Columns, &dosa.ColumnDefinition{Name: "bar", Type: dosa.Int64}) 48 49 invalidColumnType := getValidEntityDefinition() 50 invalidColumnType.Columns[0].Type = dosa.Invalid 51 52 nilPK := getValidEntityDefinition() 53 nilPK.Key = nil 54 55 noPartitionKey := getValidEntityDefinition() 56 noPartitionKey.Key.PartitionKeys = []string{} 57 58 invalidPartitionKeyName := getValidEntityDefinition() 59 invalidPartitionKeyName.Key.PartitionKeys[0] = "fox" 60 61 dupParitionKeyNames := getValidEntityDefinition() 62 dupParitionKeyNames.Key.PartitionKeys = append(dupParitionKeyNames.Key.PartitionKeys, "foo") 63 64 nilClusteringKey := getValidEntityDefinition() 65 nilClusteringKey.Key.ClusteringKeys = append(nilClusteringKey.Key.ClusteringKeys, nil) 66 67 invalidClusteringKeyName := getValidEntityDefinition() 68 invalidClusteringKeyName.Key.ClusteringKeys[0].Name = "fox" 69 70 dupClusteringKeyName := getValidEntityDefinition() 71 dupClusteringKeyName.Key.ClusteringKeys[0].Name = "foo" 72 73 noClusteringKey := getValidEntityDefinition() 74 noClusteringKey.Key.ClusteringKeys = []*dosa.ClusteringKey{} 75 76 data := []testData{ 77 { 78 e: nil, 79 valid: false, 80 msg: "EntityDefinition is nil", 81 }, 82 { 83 e: invalidName, 84 valid: false, 85 msg: "name must contain only", 86 }, 87 { 88 e: nilColumn, 89 valid: false, 90 msg: "has nil column", 91 }, 92 { 93 e: invalidColumnName, 94 valid: false, 95 msg: "has invalid column name", 96 }, 97 { 98 e: dupColumnNames, 99 valid: false, 100 msg: "duplicated column found", 101 }, 102 { 103 e: invalidColumnType, 104 valid: false, 105 msg: "invalid type for column", 106 }, 107 { 108 e: invalidColumnType, 109 valid: false, 110 msg: "\"foo\"", 111 }, 112 { 113 e: nilPK, 114 valid: false, 115 msg: "nil primary key", 116 }, 117 { 118 e: noPartitionKey, 119 valid: false, 120 msg: "does not have partition key", 121 }, 122 { 123 e: invalidPartitionKeyName, 124 valid: false, 125 msg: "partition key does not refer to a column", 126 }, 127 { 128 e: invalidPartitionKeyName, 129 valid: false, 130 msg: "\"fox\"", 131 }, 132 { 133 e: dupParitionKeyNames, 134 valid: false, 135 msg: "a column cannot be used twice in key", 136 }, 137 { 138 e: dupParitionKeyNames, 139 valid: false, 140 msg: "\"foo\"", 141 }, 142 { 143 e: invalidClusteringKeyName, 144 valid: false, 145 msg: "\"fox\"", 146 }, 147 { 148 e: invalidClusteringKeyName, 149 valid: false, 150 msg: "does not refer to", 151 }, 152 { 153 e: dupClusteringKeyName, 154 valid: false, 155 msg: "a column cannot be used twice in key", 156 }, 157 { 158 e: getValidEntityDefinition(), 159 valid: true, 160 msg: "should be a valid EntityDefinition", 161 }, 162 { 163 e: noClusteringKey, 164 valid: true, 165 msg: "no clustering key is ok", 166 }, 167 { 168 e: nilClusteringKey, 169 valid: false, 170 msg: "nil clustering key", 171 }, 172 } 173 174 for _, entry := range data { 175 err := entry.e.EnsureValid() 176 if entry.valid { 177 assert.NoError(t, err, entry.msg) 178 } else { 179 assert.Error(t, err, entry.msg) 180 assert.Contains(t, err.Error(), entry.msg) 181 } 182 } 183 } 184 185 func TestEntityDefinitionEnsureValidForIndex(t *testing.T) { 186 type testData struct { 187 e *dosa.EntityDefinition 188 valid bool 189 msg string 190 } 191 192 invalidName := getValidEntityDefinition() 193 invalidName.Indexes["index3=1123"] = invalidName.Indexes["index1"] 194 195 nilPK := getValidEntityDefinition() 196 nilPK.Indexes["index1"].Key = nil 197 198 nilIndex := getValidEntityDefinition() 199 nilIndex.Indexes["index1"] = nil 200 201 noPartitionKey := getValidEntityDefinition() 202 noPartitionKey.Indexes["index1"].Key.PartitionKeys = []string{} 203 204 invalidPartitionKeyName := getValidEntityDefinition() 205 invalidPartitionKeyName.Indexes["index1"].Key.PartitionKeys[0] = "fox" 206 207 dupParitionKeyNames := getValidEntityDefinition() 208 dupParitionKeyNames.Indexes["index1"].Key.PartitionKeys = append(dupParitionKeyNames.Key.PartitionKeys, "foo") 209 210 nilClusteringKey := getValidEntityDefinition() 211 nilClusteringKey.Indexes["index1"].Key.ClusteringKeys = append(nilClusteringKey.Key.ClusteringKeys, nil) 212 213 invalidClusteringKeyName := getValidEntityDefinition() 214 invalidClusteringKeyName.Indexes["index1"].Key.ClusteringKeys[0].Name = "fox" 215 216 dupClusteringKeyName := getValidEntityDefinition() 217 dupClusteringKeyName.Indexes["index1"].Key.ClusteringKeys[0].Name = "qux" 218 219 noClusteringKey := getValidEntityDefinition() 220 noClusteringKey.Indexes["index1"].Key.ClusteringKeys = []*dosa.ClusteringKey{} 221 222 data := []testData{ 223 { 224 e: invalidName, 225 valid: false, 226 msg: "name must contain only", 227 }, 228 { 229 e: nilPK, 230 valid: false, 231 msg: "nil key", 232 }, 233 { 234 e: nilIndex, 235 valid: false, 236 msg: "is nil", 237 }, 238 { 239 e: noPartitionKey, 240 valid: false, 241 msg: "does not have partition key", 242 }, 243 { 244 e: invalidPartitionKeyName, 245 valid: false, 246 msg: "partition key does not refer to a column", 247 }, 248 { 249 e: invalidPartitionKeyName, 250 valid: false, 251 msg: "\"fox\"", 252 }, 253 { 254 e: dupParitionKeyNames, 255 valid: false, 256 msg: "a column cannot be used twice in index key", 257 }, 258 { 259 e: dupParitionKeyNames, 260 valid: false, 261 msg: "\"foo\"", 262 }, 263 { 264 e: invalidClusteringKeyName, 265 valid: false, 266 msg: "\"fox\"", 267 }, 268 { 269 e: invalidClusteringKeyName, 270 valid: false, 271 msg: "does not refer to", 272 }, 273 { 274 e: dupClusteringKeyName, 275 valid: false, 276 msg: "a column cannot be used twice in index key", 277 }, 278 { 279 e: noClusteringKey, 280 valid: true, 281 msg: "no clustering key is ok", 282 }, 283 { 284 e: nilClusteringKey, 285 valid: false, 286 msg: "nil clustering key", 287 }, 288 } 289 290 for _, entry := range data { 291 err := entry.e.EnsureValid() 292 if entry.valid { 293 assert.NoError(t, err, entry.msg) 294 } else { 295 assert.Error(t, err, entry.msg) 296 assert.Contains(t, err.Error(), entry.msg) 297 } 298 } 299 } 300 301 func TestEntityDefinitionHelpers(t *testing.T) { 302 ed := getValidEntityDefinition() 303 304 expectedColumnTypes := map[string]dosa.Type{ 305 "foo": dosa.TUUID, 306 "bar": dosa.Int64, 307 "qux": dosa.Blob, 308 } 309 assert.Equal(t, expectedColumnTypes, ed.ColumnTypes()) 310 311 expectedPartitionKeySet := map[string]struct{}{"foo": {}} 312 assert.Equal(t, expectedPartitionKeySet, ed.PartitionKeySet()) 313 assert.Equal(t, expectedPartitionKeySet, ed.Key.PartitionKeySet()) 314 315 expectedClusteringKeySet := map[string]struct{}{"bar": {}} 316 assert.Equal(t, expectedClusteringKeySet, ed.Key.ClusteringKeySet()) 317 318 expectedKeySet := map[string]struct{}{"foo": {}, "bar": {}} 319 assert.Equal(t, expectedKeySet, ed.KeySet()) 320 } 321 322 func getValidEntityDefinition() *dosa.EntityDefinition { 323 return &dosa.EntityDefinition{ 324 Name: "testentity", 325 Key: &dosa.PrimaryKey{ 326 PartitionKeys: []string{"foo"}, 327 ClusteringKeys: []*dosa.ClusteringKey{ 328 { 329 Name: "bar", 330 Descending: true, 331 }, 332 }, 333 }, 334 Indexes: map[string]*dosa.IndexDefinition{ 335 "index1": { 336 Key: &dosa.PrimaryKey{ 337 PartitionKeys: []string{"qux"}, 338 ClusteringKeys: []*dosa.ClusteringKey{ 339 { 340 Name: "bar", 341 Descending: true, 342 }, 343 }, 344 }, 345 }, 346 347 "index2": { 348 Key: &dosa.PrimaryKey{ 349 PartitionKeys: []string{"bar"}, 350 }, 351 }, 352 }, 353 Columns: []*dosa.ColumnDefinition{ 354 { 355 Name: "foo", 356 Type: dosa.TUUID, 357 }, 358 { 359 Name: "bar", 360 Type: dosa.Int64, 361 }, 362 { 363 Name: "qux", 364 Type: dosa.Blob, 365 }, 366 }, 367 } 368 } 369 370 func TestEntityDefinitionIsCompatible(t *testing.T) { 371 validEd := getValidEntityDefinition() 372 // entity name not match 373 errEd := getValidEntityDefinition() 374 errEd.Name = errEd.Name + "error" 375 err := validEd.IsCompatible(errEd) 376 assert.Error(t, err) 377 assert.Contains(t, err.Error(), "entity name") 378 379 // partition key's size doesn't match 380 // less 381 errEd = getValidEntityDefinition() 382 errEd.Key.PartitionKeys = []string{} 383 err = validEd.IsCompatible(errEd) 384 assert.Error(t, err) 385 assert.Contains(t, err.Error(), "partition") 386 387 // more 388 errEd = getValidEntityDefinition() 389 errEd.Key.PartitionKeys = append(errEd.Key.PartitionKeys, "bar") 390 err = validEd.IsCompatible(errEd) 391 assert.Error(t, err) 392 assert.Contains(t, err.Error(), "partition") 393 394 // not same partition key 395 errEd = getValidEntityDefinition() 396 errEd.Key.PartitionKeys = []string{"bar"} 397 err = validEd.IsCompatible(errEd) 398 assert.Error(t, err) 399 assert.Contains(t, err.Error(), "partition") 400 401 // clustering key's size doesn't match 402 // less 403 errEd = getValidEntityDefinition() 404 errEd.Key.ClusteringKeys = nil 405 err = validEd.IsCompatible(errEd) 406 assert.Error(t, err) 407 assert.Contains(t, err.Error(), "clustering") 408 409 // more 410 errEd = getValidEntityDefinition() 411 errEd.Key.ClusteringKeys = append(errEd.Key.ClusteringKeys, &dosa.ClusteringKey{Name: "qux", Descending: false}) 412 err = validEd.IsCompatible(errEd) 413 assert.Error(t, err) 414 assert.Contains(t, err.Error(), "clustering") 415 416 // empty clustering key 417 errEd = getValidEntityDefinition() 418 errEd.Key.ClusteringKeys = nil 419 420 errEd1 := getValidEntityDefinition() 421 errEd1.Key.ClusteringKeys = make([]*dosa.ClusteringKey, 0) 422 err = errEd.IsCompatible(errEd1) 423 assert.NoError(t, err) 424 425 // not same clustering key 426 // name not match 427 errEd = getValidEntityDefinition() 428 errEd.Key.ClusteringKeys[0].Name = "qux" 429 err = validEd.IsCompatible(errEd) 430 assert.Error(t, err) 431 assert.Contains(t, err.Error(), "clustering") 432 433 // descending not match 434 errEd = getValidEntityDefinition() 435 errEd.Key.ClusteringKeys[0].Descending = !errEd.Key.ClusteringKeys[0].Descending 436 err = validEd.IsCompatible(errEd) 437 assert.Error(t, err) 438 assert.Contains(t, err.Error(), "clustering") 439 440 // column size is less 441 errEd = getValidEntityDefinition() 442 errEd.Columns = append(errEd.Columns, &dosa.ColumnDefinition{Name: "abc", Type: dosa.Bool}) 443 err = validEd.IsCompatible(errEd) 444 assert.Error(t, err) 445 assert.Contains(t, err.Error(), "column") 446 447 // column not match 448 // name not match 449 errEd = getValidEntityDefinition() 450 errEd.Columns[0].Name = errEd.Columns[0].Name + "error" 451 err = validEd.IsCompatible(errEd) 452 assert.Error(t, err) 453 assert.Contains(t, err.Error(), "column") 454 455 // type not match 456 errEd = getValidEntityDefinition() 457 errEd.Columns[0].Type = dosa.Invalid 458 err = validEd.IsCompatible(errEd) 459 assert.Error(t, err) 460 assert.Contains(t, err.Error(), "type") 461 462 // index not match 463 errEd = getValidEntityDefinition() 464 errEd.Indexes["index1"] = &dosa.IndexDefinition{ 465 Key: &dosa.PrimaryKey{ 466 PartitionKeys: []string{"bar, qux"}, 467 }, 468 } 469 err = validEd.IsCompatible(errEd) 470 assert.Error(t, err) 471 assert.Contains(t, err.Error(), "index") 472 473 // same entity 474 // name not match 475 aEd := getValidEntityDefinition() 476 err = validEd.IsCompatible(aEd) 477 assert.NoError(t, err) 478 // reverse 479 err = aEd.IsCompatible(validEd) 480 assert.NoError(t, err) 481 482 // add new column 483 aEd = getValidEntityDefinition() 484 aEd.Columns = append(aEd.Columns, &dosa.ColumnDefinition{Name: "col", Type: dosa.Bool}) 485 err = aEd.IsCompatible(validEd) 486 assert.NoError(t, err) 487 488 // reverse 489 err = validEd.IsCompatible(aEd) 490 assert.Error(t, err) 491 492 // add new index 493 aEd = getValidEntityDefinition() 494 aEd.Indexes["newindex"] = &dosa.IndexDefinition{ 495 Key: &dosa.PrimaryKey{ 496 PartitionKeys: []string{"bar, qux"}, 497 }, 498 } 499 500 err = aEd.IsCompatible(validEd) 501 assert.NoError(t, err) 502 } 503 504 func TestEntityDefinition_FindColumnDefinition(t *testing.T) { 505 ed := getValidEntityDefinition() 506 507 // for each known column, make sure we find the column definition with the same name 508 for _, name := range []string{"foo", "bar", "qux"} { 509 assert.Equal(t, name, ed.FindColumnDefinition(name).Name) 510 } 511 512 assert.Nil(t, ed.FindColumnDefinition("notacolumn")) 513 } 514 515 func TestClone(t *testing.T) { 516 ed := getValidEntityDefinition() 517 ed1 := ed.Clone() 518 assert.Equal(t, ed, ed1) 519 }