github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/m3ninx/doc/document_test.go (about) 1 // Copyright (c) 2018 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 doc 22 23 import ( 24 "fmt" 25 "sort" 26 "testing" 27 28 "github.com/stretchr/testify/require" 29 ) 30 31 func TestSortingFields(t *testing.T) { 32 tests := []struct { 33 name string 34 input, expected Fields 35 }{ 36 { 37 name: "empty list should be unchanged", 38 input: Fields{}, 39 expected: Fields{}, 40 }, 41 { 42 name: "sorted fields should remain sorted", 43 input: Fields{ 44 Field{ 45 Name: []byte("apple"), 46 Value: []byte("red"), 47 }, 48 Field{ 49 Name: []byte("banana"), 50 Value: []byte("yellow"), 51 }, 52 }, 53 expected: Fields{ 54 Field{ 55 Name: []byte("apple"), 56 Value: []byte("red"), 57 }, 58 Field{ 59 Name: []byte("banana"), 60 Value: []byte("yellow"), 61 }, 62 }, 63 }, 64 { 65 name: "unsorted fields should be sorted", 66 input: Fields{ 67 Field{ 68 Name: []byte("banana"), 69 Value: []byte("yellow"), 70 }, 71 Field{ 72 Name: []byte("apple"), 73 Value: []byte("red"), 74 }, 75 }, 76 expected: Fields{ 77 Field{ 78 Name: []byte("apple"), 79 Value: []byte("red"), 80 }, 81 Field{ 82 Name: []byte("banana"), 83 Value: []byte("yellow"), 84 }, 85 }, 86 }, 87 } 88 89 for _, test := range tests { 90 t.Run(test.name, func(t *testing.T) { 91 actual := test.input 92 sort.Sort(actual) 93 require.Equal(t, test.expected, actual) 94 }) 95 } 96 } 97 98 func TestDocumentGetField(t *testing.T) { 99 tests := []struct { 100 name string 101 input Metadata 102 fieldName []byte 103 expectedOk bool 104 expectedVal []byte 105 }{ 106 { 107 name: "get existing field", 108 input: Metadata{ 109 Fields: []Field{ 110 Field{ 111 Name: []byte("apple"), 112 Value: []byte("red"), 113 }, 114 }, 115 }, 116 fieldName: []byte("apple"), 117 expectedOk: true, 118 expectedVal: []byte("red"), 119 }, 120 { 121 name: "get nonexisting field", 122 input: Metadata{ 123 Fields: []Field{ 124 Field{ 125 Name: []byte("apple"), 126 Value: []byte("red"), 127 }, 128 }, 129 }, 130 fieldName: []byte("banana"), 131 expectedOk: false, 132 }, 133 } 134 135 for _, test := range tests { 136 t.Run(test.name, func(t *testing.T) { 137 val, ok := test.input.Get(test.fieldName) 138 if test.expectedOk { 139 require.True(t, ok) 140 require.Equal(t, test.expectedVal, val) 141 return 142 } 143 require.False(t, ok) 144 }) 145 } 146 } 147 148 func TestDocumentCompare(t *testing.T) { 149 tests := []struct { 150 name string 151 l, r Metadata 152 expected int 153 }{ 154 { 155 name: "empty documents are equal", 156 l: Metadata{}, 157 r: Metadata{}, 158 expected: 0, 159 }, 160 { 161 name: "documents with the same id and the same fields in the same order are equal", 162 l: Metadata{ 163 ID: []byte("831992"), 164 Fields: []Field{ 165 Field{ 166 Name: []byte("apple"), 167 Value: []byte("red"), 168 }, 169 Field{ 170 Name: []byte("banana"), 171 Value: []byte("yellow"), 172 }, 173 }, 174 }, 175 r: Metadata{ 176 ID: []byte("831992"), 177 Fields: []Field{ 178 Field{ 179 Name: []byte("apple"), 180 Value: []byte("red"), 181 }, 182 Field{ 183 Name: []byte("banana"), 184 Value: []byte("yellow"), 185 }, 186 }, 187 }, 188 expected: 0, 189 }, 190 { 191 name: "documents are ordered by their IDs", 192 l: Metadata{ 193 ID: []byte("831992"), 194 Fields: []Field{ 195 Field{ 196 Name: []byte("banana"), 197 Value: []byte("yellow"), 198 }, 199 }, 200 }, 201 r: Metadata{ 202 ID: []byte("831991"), 203 Fields: []Field{ 204 Field{ 205 Name: []byte("apple"), 206 Value: []byte("red"), 207 }, 208 }, 209 }, 210 expected: 1, 211 }, 212 { 213 name: "documents are ordered by their field names", 214 l: Metadata{ 215 ID: []byte("831992"), 216 Fields: []Field{ 217 Field{ 218 Name: []byte("banana"), 219 Value: []byte("yellow"), 220 }, 221 }, 222 }, 223 r: Metadata{ 224 ID: []byte("831992"), 225 Fields: []Field{ 226 Field{ 227 Name: []byte("apple"), 228 Value: []byte("red"), 229 }, 230 }, 231 }, 232 expected: 1, 233 }, 234 { 235 name: "documents are ordered by their field values", 236 l: Metadata{ 237 ID: []byte("831992"), 238 Fields: []Field{ 239 Field{ 240 Name: []byte("apple"), 241 Value: []byte("green"), 242 }, 243 }, 244 }, 245 r: Metadata{ 246 ID: []byte("831992"), 247 Fields: []Field{ 248 Field{ 249 Name: []byte("apple"), 250 Value: []byte("red"), 251 }, 252 }, 253 }, 254 expected: -1, 255 }, 256 { 257 name: "documents are ordered by their lengths", 258 l: Metadata{ 259 ID: []byte("831992"), 260 Fields: []Field{ 261 Field{ 262 Name: []byte("apple"), 263 Value: []byte("red"), 264 }, 265 }, 266 }, 267 r: Metadata{ 268 ID: []byte("831992"), 269 Fields: []Field{ 270 Field{ 271 Name: []byte("apple"), 272 Value: []byte("red"), 273 }, 274 Field{ 275 Name: []byte("banana"), 276 Value: []byte("yellow"), 277 }, 278 }, 279 }, 280 expected: -1, 281 }, 282 } 283 284 for _, test := range tests { 285 t.Run(test.name, func(t *testing.T) { 286 require.Equal(t, test.expected, test.l.Compare(test.r)) 287 }) 288 } 289 } 290 func TestDocumentEquality(t *testing.T) { 291 tests := []struct { 292 name string 293 l, r Metadata 294 expected bool 295 }{ 296 { 297 name: "empty documents are equal", 298 l: Metadata{}, 299 r: Metadata{}, 300 expected: true, 301 }, 302 { 303 name: "documents with the same fields in the same order are equal", 304 l: Metadata{ 305 ID: []byte("831992"), 306 Fields: []Field{ 307 Field{ 308 Name: []byte("apple"), 309 Value: []byte("red"), 310 }, 311 Field{ 312 Name: []byte("banana"), 313 Value: []byte("yellow"), 314 }, 315 }, 316 }, 317 r: Metadata{ 318 ID: []byte("831992"), 319 Fields: []Field{ 320 Field{ 321 Name: []byte("apple"), 322 Value: []byte("red"), 323 }, 324 Field{ 325 Name: []byte("banana"), 326 Value: []byte("yellow"), 327 }, 328 }, 329 }, 330 expected: true, 331 }, 332 { 333 name: "documents with the same fields in different order are equal", 334 l: Metadata{ 335 ID: []byte("831992"), 336 Fields: []Field{ 337 Field{ 338 Name: []byte("banana"), 339 Value: []byte("yellow"), 340 }, 341 Field{ 342 Name: []byte("apple"), 343 Value: []byte("red"), 344 }, 345 }, 346 }, 347 r: Metadata{ 348 ID: []byte("831992"), 349 Fields: []Field{ 350 Field{ 351 Name: []byte("apple"), 352 Value: []byte("red"), 353 }, 354 Field{ 355 Name: []byte("banana"), 356 Value: []byte("yellow"), 357 }, 358 }, 359 }, 360 expected: true, 361 }, 362 { 363 name: "documents with different fields are unequal", 364 l: Metadata{ 365 ID: []byte("831992"), 366 Fields: []Field{ 367 Field{ 368 Name: []byte("apple"), 369 Value: []byte("red"), 370 }, 371 Field{ 372 Name: []byte("banana"), 373 Value: []byte("yellow"), 374 }, 375 }, 376 }, 377 r: Metadata{ 378 ID: []byte("831992"), 379 Fields: []Field{ 380 Field{ 381 Name: []byte("apple"), 382 Value: []byte("red"), 383 }, 384 Field{ 385 Name: []byte("carrot"), 386 Value: []byte("orange"), 387 }, 388 }, 389 }, 390 expected: false, 391 }, 392 { 393 name: "documents with different IDs are unequal", 394 l: Metadata{ 395 ID: []byte("831992"), 396 Fields: []Field{ 397 Field{ 398 Name: []byte("apple"), 399 Value: []byte("red"), 400 }, 401 }, 402 }, 403 r: Metadata{ 404 ID: []byte("080292"), 405 Fields: []Field{ 406 Field{ 407 Name: []byte("apple"), 408 Value: []byte("red"), 409 }, 410 }, 411 }, 412 expected: false, 413 }, 414 } 415 416 for _, test := range tests { 417 t.Run(test.name, func(t *testing.T) { 418 require.Equal(t, test.expected, test.l.Equal(test.r)) 419 }) 420 } 421 } 422 423 func TestDocumentValidation(t *testing.T) { 424 tests := []struct { 425 name string 426 input Metadata 427 expectedErr bool 428 }{ 429 { 430 name: "empty document", 431 input: Metadata{}, 432 expectedErr: true, 433 }, 434 { 435 name: "empty document w/ ID", 436 input: Metadata{ 437 ID: []byte("foobar"), 438 }, 439 expectedErr: false, 440 }, 441 { 442 name: "invalid UTF-8 in field name", 443 input: Metadata{ 444 Fields: []Field{ 445 Field{ 446 Name: []byte("\xff"), 447 Value: []byte("bar"), 448 }, 449 }, 450 }, 451 expectedErr: true, 452 }, 453 { 454 name: "invalid UTF-8 in field value", 455 input: Metadata{ 456 Fields: []Field{ 457 Field{ 458 Name: []byte("\xff"), 459 Value: []byte("bar"), 460 }, 461 }, 462 }, 463 expectedErr: true, 464 }, 465 { 466 name: "document contains field with reserved field name", 467 input: Metadata{ 468 Fields: []Field{ 469 Field{ 470 Name: []byte("apple"), 471 Value: []byte("red"), 472 }, 473 Field{ 474 Name: IDReservedFieldName, 475 Value: []byte("123"), 476 }, 477 }, 478 }, 479 expectedErr: true, 480 }, 481 { 482 name: "valid document", 483 input: Metadata{ 484 Fields: []Field{ 485 Field{ 486 Name: []byte("apple"), 487 Value: []byte("red"), 488 }, 489 }, 490 }, 491 expectedErr: false, 492 }, 493 } 494 495 for _, test := range tests { 496 t.Run(test.name, func(t *testing.T) { 497 err := test.input.Validate() 498 if test.expectedErr { 499 require.Error(t, err) 500 return 501 } 502 require.NoError(t, err) 503 }) 504 } 505 } 506 507 func TestDocumentHasID(t *testing.T) { 508 tests := []struct { 509 name string 510 input Metadata 511 expected bool 512 }{ 513 { 514 name: "nil ID", 515 input: Metadata{ 516 ID: nil, 517 }, 518 expected: false, 519 }, 520 { 521 name: "zero-length ID", 522 input: Metadata{ 523 ID: make([]byte, 0, 16), 524 }, 525 expected: false, 526 }, 527 { 528 name: "valid ID", 529 input: Metadata{ 530 ID: []byte("831992"), 531 }, 532 expected: true, 533 }, 534 } 535 536 for _, test := range tests { 537 t.Run(test.name, func(t *testing.T) { 538 require.Equal(t, test.expected, test.input.HasID()) 539 }) 540 } 541 } 542 543 func TestSortingDocuments(t *testing.T) { 544 tests := []struct { 545 name string 546 input, expected Documents 547 }{ 548 { 549 name: "unordered documents", 550 input: Documents{ 551 Metadata{ 552 ID: []byte("831992"), 553 Fields: []Field{ 554 Field{ 555 Name: []byte("banana"), 556 Value: []byte("yellow"), 557 }, 558 }, 559 }, 560 Metadata{ 561 ID: []byte("831992"), 562 Fields: []Field{ 563 Field{ 564 Name: []byte("apple"), 565 Value: []byte("red"), 566 }, 567 }, 568 }, 569 }, 570 expected: Documents{ 571 Metadata{ 572 ID: []byte("831992"), 573 Fields: []Field{ 574 Field{ 575 Name: []byte("apple"), 576 Value: []byte("red"), 577 }, 578 }, 579 }, 580 Metadata{ 581 ID: []byte("831992"), 582 Fields: []Field{ 583 Field{ 584 Name: []byte("banana"), 585 Value: []byte("yellow"), 586 }, 587 }, 588 }, 589 }, 590 }, 591 } 592 593 for _, test := range tests { 594 t.Run(test.name, func(t *testing.T) { 595 actual := test.input 596 sort.Sort(actual) 597 require.Equal(t, len(test.expected), len(actual)) 598 fmt.Println(actual) 599 for i := range test.expected { 600 require.True(t, test.expected[i].Equal(actual[i])) 601 } 602 }) 603 } 604 }