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