github.com/trigonella/mattermost-server@v5.11.1+incompatible/model/utils_test.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package model 5 6 import ( 7 "fmt" 8 "net/http" 9 "reflect" 10 "strings" 11 "testing" 12 "time" 13 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 ) 17 18 func TestNewId(t *testing.T) { 19 for i := 0; i < 1000; i++ { 20 id := NewId() 21 if len(id) > 26 { 22 t.Fatal("ids shouldn't be longer than 26 chars") 23 } 24 } 25 } 26 27 func TestRandomString(t *testing.T) { 28 for i := 0; i < 1000; i++ { 29 r := NewRandomString(32) 30 if len(r) != 32 { 31 t.Fatal("should be 32 chars") 32 } 33 } 34 } 35 36 func TestGetMillisForTime(t *testing.T) { 37 thisTimeMillis := int64(1471219200000) 38 thisTime := time.Date(2016, time.August, 15, 0, 0, 0, 0, time.UTC) 39 40 result := GetMillisForTime(thisTime) 41 42 if thisTimeMillis != result { 43 t.Fatalf(fmt.Sprintf("millis are not the same: %d and %d", thisTimeMillis, result)) 44 } 45 } 46 47 func TestPadDateStringZeros(t *testing.T) { 48 for _, testCase := range []struct { 49 Name string 50 Input string 51 Expected string 52 }{ 53 { 54 Name: "Valid date", 55 Input: "2016-08-01", 56 Expected: "2016-08-01", 57 }, 58 { 59 Name: "Valid date but requires padding of zero", 60 Input: "2016-8-1", 61 Expected: "2016-08-01", 62 }, 63 } { 64 t.Run(testCase.Name, func(t *testing.T) { 65 assert.Equal(t, testCase.Expected, PadDateStringZeros(testCase.Input)) 66 }) 67 } 68 } 69 70 func TestAppError(t *testing.T) { 71 err := NewAppError("TestAppError", "message", nil, "", http.StatusInternalServerError) 72 json := err.ToJson() 73 rerr := AppErrorFromJson(strings.NewReader(json)) 74 require.Equal(t, err.Message, rerr.Message) 75 76 t.Log(err.Error()) 77 } 78 79 func TestAppErrorJunk(t *testing.T) { 80 rerr := AppErrorFromJson(strings.NewReader("<html><body>This is a broken test</body></html>")) 81 require.Equal(t, "body: <html><body>This is a broken test</body></html>", rerr.DetailedError) 82 } 83 84 func TestCopyStringMap(t *testing.T) { 85 itemKey := "item1" 86 originalMap := make(map[string]string) 87 originalMap[itemKey] = "val1" 88 89 copyMap := CopyStringMap(originalMap) 90 copyMap[itemKey] = "changed" 91 92 assert.Equal(t, "val1", originalMap[itemKey]) 93 } 94 95 func TestMapJson(t *testing.T) { 96 97 m := make(map[string]string) 98 m["id"] = "test_id" 99 json := MapToJson(m) 100 101 rm := MapFromJson(strings.NewReader(json)) 102 103 if rm["id"] != "test_id" { 104 t.Fatal("map should be valid") 105 } 106 107 rm2 := MapFromJson(strings.NewReader("")) 108 if len(rm2) > 0 { 109 t.Fatal("make should be ivalid") 110 } 111 } 112 113 func TestIsValidEmail(t *testing.T) { 114 for _, testCase := range []struct { 115 Input string 116 Expected bool 117 }{ 118 { 119 Input: "corey", 120 Expected: false, 121 }, 122 { 123 Input: "corey@example.com", 124 Expected: true, 125 }, 126 { 127 Input: "corey+test@example.com", 128 Expected: true, 129 }, 130 { 131 Input: "@corey+test@example.com", 132 Expected: false, 133 }, 134 { 135 Input: "firstname.lastname@example.com", 136 Expected: true, 137 }, 138 { 139 Input: "firstname.lastname@subdomain.example.com", 140 Expected: true, 141 }, 142 { 143 Input: "123454567@domain.com", 144 Expected: true, 145 }, 146 { 147 Input: "email@domain-one.com", 148 Expected: true, 149 }, 150 { 151 Input: "email@domain.co.jp", 152 Expected: true, 153 }, 154 { 155 Input: "firstname-lastname@domain.com", 156 Expected: true, 157 }, 158 { 159 Input: "@domain.com", 160 Expected: false, 161 }, 162 { 163 Input: "Billy Bob <billy@example.com>", 164 Expected: false, 165 }, 166 { 167 Input: "email.domain.com", 168 Expected: false, 169 }, 170 { 171 Input: "email.@domain.com", 172 Expected: false, 173 }, 174 { 175 Input: "email@domain@domain.com", 176 Expected: false, 177 }, 178 { 179 Input: "(email@domain.com)", 180 Expected: false, 181 }, 182 { 183 Input: "email@汤.中国", 184 Expected: true, 185 }, 186 { 187 Input: "email1@domain.com, email2@domain.com", 188 Expected: false, 189 }, 190 } { 191 t.Run(testCase.Input, func(t *testing.T) { 192 assert.Equal(t, testCase.Expected, IsValidEmail(testCase.Input)) 193 }) 194 } 195 } 196 197 func TestValidLower(t *testing.T) { 198 if !IsLower("corey+test@hulen.com") { 199 t.Error("should be valid") 200 } 201 202 if IsLower("Corey+test@hulen.com") { 203 t.Error("should be invalid") 204 } 205 } 206 207 func TestEtag(t *testing.T) { 208 etag := Etag("hello", 24) 209 require.NotEqual(t, "", etag) 210 } 211 212 var hashtags = map[string]string{ 213 "#test": "#test", 214 "test": "", 215 "#test123": "#test123", 216 "#123test123": "", 217 "#test-test": "#test-test", 218 "#test?": "#test", 219 "hi #there": "#there", 220 "#bug #idea": "#bug #idea", 221 "#bug or #gif!": "#bug #gif", 222 "#hüllo": "#hüllo", 223 "#?test": "", 224 "#-test": "", 225 "#yo_yo": "#yo_yo", 226 "(#brakets)": "#brakets", 227 ")#stekarb(": "#stekarb", 228 "<#less_than<": "#less_than", 229 ">#greater_than>": "#greater_than", 230 "-#minus-": "#minus", 231 "_#under_": "#under", 232 "+#plus+": "#plus", 233 "=#equals=": "#equals", 234 "%#pct%": "#pct", 235 "&#and&": "#and", 236 "^#hat^": "#hat", 237 "##brown#": "#brown", 238 "*#star*": "#star", 239 "|#pipe|": "#pipe", 240 ":#colon:": "#colon", 241 ";#semi;": "#semi", 242 "#Mötley;": "#Mötley", 243 ".#period.": "#period", 244 "¿#upside¿": "#upside", 245 "\"#quote\"": "#quote", 246 "/#slash/": "#slash", 247 "\\#backslash\\": "#backslash", 248 "#a": "", 249 "#1": "", 250 "foo#bar": "", 251 } 252 253 func TestStringArray_Equal(t *testing.T) { 254 for name, tc := range map[string]struct { 255 Array1 StringArray 256 Array2 StringArray 257 Expected bool 258 }{ 259 "Empty": { 260 nil, 261 nil, 262 true, 263 }, 264 "EqualLength_EqualValue": { 265 StringArray{"123"}, 266 StringArray{"123"}, 267 true, 268 }, 269 "DifferentLength": { 270 StringArray{"123"}, 271 StringArray{"123", "abc"}, 272 false, 273 }, 274 "DifferentValues_EqualLength": { 275 StringArray{"123"}, 276 StringArray{"abc"}, 277 false, 278 }, 279 "EqualLength_EqualValues": { 280 StringArray{"123", "abc"}, 281 StringArray{"123", "abc"}, 282 true, 283 }, 284 "EqualLength_EqualValues_DifferentOrder": { 285 StringArray{"abc", "123"}, 286 StringArray{"123", "abc"}, 287 false, 288 }, 289 } { 290 t.Run(name, func(t *testing.T) { 291 assert.Equal(t, tc.Expected, tc.Array1.Equals(tc.Array2)) 292 }) 293 } 294 } 295 296 func TestParseHashtags(t *testing.T) { 297 for input, output := range hashtags { 298 if o, _ := ParseHashtags(input); o != output { 299 t.Fatal("failed to parse hashtags from input=" + input + " expected=" + output + " actual=" + o) 300 } 301 } 302 } 303 304 func TestIsValidAlphaNum(t *testing.T) { 305 cases := []struct { 306 Input string 307 Result bool 308 }{ 309 { 310 Input: "test", 311 Result: true, 312 }, 313 { 314 Input: "test-name", 315 Result: true, 316 }, 317 { 318 Input: "test--name", 319 Result: true, 320 }, 321 { 322 Input: "test__name", 323 Result: true, 324 }, 325 { 326 Input: "-", 327 Result: false, 328 }, 329 { 330 Input: "__", 331 Result: false, 332 }, 333 { 334 Input: "test-", 335 Result: false, 336 }, 337 { 338 Input: "test--", 339 Result: false, 340 }, 341 { 342 Input: "test__", 343 Result: false, 344 }, 345 { 346 Input: "test:name", 347 Result: false, 348 }, 349 } 350 351 for _, tc := range cases { 352 actual := IsValidAlphaNum(tc.Input) 353 if actual != tc.Result { 354 t.Fatalf("case: %v\tshould returned: %#v", tc, tc.Result) 355 } 356 } 357 } 358 359 func TestGetServerIpAddress(t *testing.T) { 360 if len(GetServerIpAddress()) == 0 { 361 t.Fatal("Should find local ip address") 362 } 363 } 364 365 func TestIsValidAlphaNumHyphenUnderscore(t *testing.T) { 366 casesWithFormat := []struct { 367 Input string 368 Result bool 369 }{ 370 { 371 Input: "test", 372 Result: true, 373 }, 374 { 375 Input: "test-name", 376 Result: true, 377 }, 378 { 379 Input: "test--name", 380 Result: true, 381 }, 382 { 383 Input: "test__name", 384 Result: true, 385 }, 386 { 387 Input: "test_name", 388 Result: true, 389 }, 390 { 391 Input: "test_-name", 392 Result: true, 393 }, 394 { 395 Input: "-", 396 Result: false, 397 }, 398 { 399 Input: "__", 400 Result: false, 401 }, 402 { 403 Input: "test-", 404 Result: false, 405 }, 406 { 407 Input: "test--", 408 Result: false, 409 }, 410 { 411 Input: "test__", 412 Result: false, 413 }, 414 { 415 Input: "test:name", 416 Result: false, 417 }, 418 } 419 420 for _, tc := range casesWithFormat { 421 actual := IsValidAlphaNumHyphenUnderscore(tc.Input, true) 422 if actual != tc.Result { 423 t.Fatalf("case: %v\tshould returned: %#v", tc, tc.Result) 424 } 425 } 426 427 casesWithoutFormat := []struct { 428 Input string 429 Result bool 430 }{ 431 { 432 Input: "test", 433 Result: true, 434 }, 435 { 436 Input: "test-name", 437 Result: true, 438 }, 439 { 440 Input: "test--name", 441 Result: true, 442 }, 443 { 444 Input: "test__name", 445 Result: true, 446 }, 447 { 448 Input: "test_name", 449 Result: true, 450 }, 451 { 452 Input: "test_-name", 453 Result: true, 454 }, 455 { 456 Input: "-", 457 Result: true, 458 }, 459 { 460 Input: "_", 461 Result: true, 462 }, 463 { 464 Input: "test-", 465 Result: true, 466 }, 467 { 468 Input: "test--", 469 Result: true, 470 }, 471 { 472 Input: "test__", 473 Result: true, 474 }, 475 { 476 Input: ".", 477 Result: false, 478 }, 479 480 { 481 Input: "test,", 482 Result: false, 483 }, 484 { 485 Input: "test:name", 486 Result: false, 487 }, 488 } 489 490 for _, tc := range casesWithoutFormat { 491 actual := IsValidAlphaNumHyphenUnderscore(tc.Input, false) 492 if actual != tc.Result { 493 t.Fatalf("case: '%v'\tshould returned: %#v", tc.Input, tc.Result) 494 } 495 } 496 } 497 498 func TestIsValidId(t *testing.T) { 499 cases := []struct { 500 Input string 501 Result bool 502 }{ 503 { 504 Input: NewId(), 505 Result: true, 506 }, 507 { 508 Input: "", 509 Result: false, 510 }, 511 { 512 Input: "junk", 513 Result: false, 514 }, 515 { 516 Input: "qwertyuiop1234567890asdfg{", 517 Result: false, 518 }, 519 { 520 Input: NewId() + "}", 521 Result: false, 522 }, 523 } 524 525 for _, tc := range cases { 526 actual := IsValidId(tc.Input) 527 if actual != tc.Result { 528 t.Fatalf("case: %v\tshould returned: %#v", tc, tc.Result) 529 } 530 } 531 } 532 533 func TestNowhereNil(t *testing.T) { 534 t.Parallel() 535 536 var nilStringPtr *string 537 var nonNilStringPtr *string = new(string) 538 var nilSlice []string 539 var nilStruct *struct{} 540 var nilMap map[bool]bool 541 542 var nowhereNilStruct = struct { 543 X *string 544 Y *string 545 }{ 546 nonNilStringPtr, 547 nonNilStringPtr, 548 } 549 var somewhereNilStruct = struct { 550 X *string 551 Y *string 552 }{ 553 nonNilStringPtr, 554 nilStringPtr, 555 } 556 557 var privateSomewhereNilStruct = struct { 558 X *string 559 y *string 560 }{ 561 nonNilStringPtr, 562 nilStringPtr, 563 } 564 565 testCases := []struct { 566 Description string 567 Value interface{} 568 Expected bool 569 }{ 570 { 571 "nil", 572 nil, 573 false, 574 }, 575 { 576 "empty string", 577 "", 578 true, 579 }, 580 { 581 "non-empty string", 582 "not empty!", 583 true, 584 }, 585 { 586 "nil string pointer", 587 nilStringPtr, 588 false, 589 }, 590 { 591 "non-nil string pointer", 592 nonNilStringPtr, 593 true, 594 }, 595 { 596 "0", 597 0, 598 true, 599 }, 600 { 601 "1", 602 1, 603 true, 604 }, 605 { 606 "0 (int64)", 607 int64(0), 608 true, 609 }, 610 { 611 "1 (int64)", 612 int64(1), 613 true, 614 }, 615 { 616 "true", 617 true, 618 true, 619 }, 620 { 621 "false", 622 false, 623 true, 624 }, 625 { 626 "nil slice", 627 nilSlice, 628 // A nil slice is observably the same as an empty slice, so allow it. 629 true, 630 }, 631 { 632 "empty slice", 633 []string{}, 634 true, 635 }, 636 { 637 "slice containing nils", 638 []*string{nil, nil}, 639 true, 640 }, 641 { 642 "nil map", 643 nilMap, 644 false, 645 }, 646 { 647 "non-nil map", 648 make(map[bool]bool), 649 true, 650 }, 651 { 652 "non-nil map containing nil", 653 map[bool]*string{true: nilStringPtr, false: nonNilStringPtr}, 654 // Map values are not checked 655 true, 656 }, 657 { 658 "nil struct", 659 nilStruct, 660 false, 661 }, 662 { 663 "empty struct", 664 struct{}{}, 665 true, 666 }, 667 { 668 "struct containing no nil", 669 nowhereNilStruct, 670 true, 671 }, 672 { 673 "struct containing nil", 674 somewhereNilStruct, 675 false, 676 }, 677 { 678 "struct pointer containing no nil", 679 &nowhereNilStruct, 680 true, 681 }, 682 { 683 "struct pointer containing nil", 684 &somewhereNilStruct, 685 false, 686 }, 687 { 688 "struct containing private nil", 689 privateSomewhereNilStruct, 690 true, 691 }, 692 { 693 "struct pointer containing private nil", 694 &privateSomewhereNilStruct, 695 true, 696 }, 697 } 698 699 for _, testCase := range testCases { 700 testCase := testCase 701 t.Run(testCase.Description, func(t *testing.T) { 702 defer func() { 703 if r := recover(); r != nil { 704 t.Errorf("panic: %v", r) 705 } 706 }() 707 708 t.Parallel() 709 require.Equal(t, testCase.Expected, checkNowhereNil(t, "value", testCase.Value)) 710 }) 711 } 712 } 713 714 // checkNowhereNil checks that the given interface value is not nil, and if a struct, that all of 715 // its public fields are also nowhere nil 716 func checkNowhereNil(t *testing.T, name string, value interface{}) bool { 717 if value == nil { 718 return false 719 } 720 721 v := reflect.ValueOf(value) 722 switch v.Type().Kind() { 723 case reflect.Ptr: 724 if v.IsNil() { 725 t.Logf("%s was nil", name) 726 return false 727 } 728 729 return checkNowhereNil(t, fmt.Sprintf("(*%s)", name), v.Elem().Interface()) 730 731 case reflect.Map: 732 if v.IsNil() { 733 t.Logf("%s was nil", name) 734 return false 735 } 736 737 // Don't check map values 738 return true 739 740 case reflect.Struct: 741 nowhereNil := true 742 for i := 0; i < v.NumField(); i++ { 743 f := v.Field(i) 744 // Ignore unexported fields 745 if v.Type().Field(i).PkgPath != "" { 746 continue 747 } 748 749 nowhereNil = nowhereNil && checkNowhereNil(t, fmt.Sprintf("%s.%s", name, v.Type().Field(i).Name), f.Interface()) 750 } 751 752 return nowhereNil 753 754 case reflect.Array: 755 fallthrough 756 case reflect.Chan: 757 fallthrough 758 case reflect.Func: 759 fallthrough 760 case reflect.Interface: 761 fallthrough 762 case reflect.UnsafePointer: 763 t.Logf("unhandled field %s, type: %s", name, v.Type().Kind()) 764 return false 765 766 default: 767 return true 768 } 769 }