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