github.com/crspeller/mattermost-server@v0.0.0-20190328001957-a200beb3d111/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 TestParseHashtags(t *testing.T) { 254 for input, output := range hashtags { 255 if o, _ := ParseHashtags(input); o != output { 256 t.Fatal("failed to parse hashtags from input=" + input + " expected=" + output + " actual=" + o) 257 } 258 } 259 } 260 261 func TestIsValidAlphaNum(t *testing.T) { 262 cases := []struct { 263 Input string 264 Result bool 265 }{ 266 { 267 Input: "test", 268 Result: true, 269 }, 270 { 271 Input: "test-name", 272 Result: true, 273 }, 274 { 275 Input: "test--name", 276 Result: true, 277 }, 278 { 279 Input: "test__name", 280 Result: true, 281 }, 282 { 283 Input: "-", 284 Result: false, 285 }, 286 { 287 Input: "__", 288 Result: false, 289 }, 290 { 291 Input: "test-", 292 Result: false, 293 }, 294 { 295 Input: "test--", 296 Result: false, 297 }, 298 { 299 Input: "test__", 300 Result: false, 301 }, 302 { 303 Input: "test:name", 304 Result: false, 305 }, 306 } 307 308 for _, tc := range cases { 309 actual := IsValidAlphaNum(tc.Input) 310 if actual != tc.Result { 311 t.Fatalf("case: %v\tshould returned: %#v", tc, tc.Result) 312 } 313 } 314 } 315 316 func TestGetServerIpAddress(t *testing.T) { 317 if len(GetServerIpAddress()) == 0 { 318 t.Fatal("Should find local ip address") 319 } 320 } 321 322 func TestIsValidAlphaNumHyphenUnderscore(t *testing.T) { 323 casesWithFormat := []struct { 324 Input string 325 Result bool 326 }{ 327 { 328 Input: "test", 329 Result: true, 330 }, 331 { 332 Input: "test-name", 333 Result: true, 334 }, 335 { 336 Input: "test--name", 337 Result: true, 338 }, 339 { 340 Input: "test__name", 341 Result: true, 342 }, 343 { 344 Input: "test_name", 345 Result: true, 346 }, 347 { 348 Input: "test_-name", 349 Result: true, 350 }, 351 { 352 Input: "-", 353 Result: false, 354 }, 355 { 356 Input: "__", 357 Result: false, 358 }, 359 { 360 Input: "test-", 361 Result: false, 362 }, 363 { 364 Input: "test--", 365 Result: false, 366 }, 367 { 368 Input: "test__", 369 Result: false, 370 }, 371 { 372 Input: "test:name", 373 Result: false, 374 }, 375 } 376 377 for _, tc := range casesWithFormat { 378 actual := IsValidAlphaNumHyphenUnderscore(tc.Input, true) 379 if actual != tc.Result { 380 t.Fatalf("case: %v\tshould returned: %#v", tc, tc.Result) 381 } 382 } 383 384 casesWithoutFormat := []struct { 385 Input string 386 Result bool 387 }{ 388 { 389 Input: "test", 390 Result: true, 391 }, 392 { 393 Input: "test-name", 394 Result: true, 395 }, 396 { 397 Input: "test--name", 398 Result: true, 399 }, 400 { 401 Input: "test__name", 402 Result: true, 403 }, 404 { 405 Input: "test_name", 406 Result: true, 407 }, 408 { 409 Input: "test_-name", 410 Result: true, 411 }, 412 { 413 Input: "-", 414 Result: true, 415 }, 416 { 417 Input: "_", 418 Result: true, 419 }, 420 { 421 Input: "test-", 422 Result: true, 423 }, 424 { 425 Input: "test--", 426 Result: true, 427 }, 428 { 429 Input: "test__", 430 Result: true, 431 }, 432 { 433 Input: ".", 434 Result: false, 435 }, 436 437 { 438 Input: "test,", 439 Result: false, 440 }, 441 { 442 Input: "test:name", 443 Result: false, 444 }, 445 } 446 447 for _, tc := range casesWithoutFormat { 448 actual := IsValidAlphaNumHyphenUnderscore(tc.Input, false) 449 if actual != tc.Result { 450 t.Fatalf("case: '%v'\tshould returned: %#v", tc.Input, tc.Result) 451 } 452 } 453 } 454 455 func TestIsValidId(t *testing.T) { 456 cases := []struct { 457 Input string 458 Result bool 459 }{ 460 { 461 Input: NewId(), 462 Result: true, 463 }, 464 { 465 Input: "", 466 Result: false, 467 }, 468 { 469 Input: "junk", 470 Result: false, 471 }, 472 { 473 Input: "qwertyuiop1234567890asdfg{", 474 Result: false, 475 }, 476 { 477 Input: NewId() + "}", 478 Result: false, 479 }, 480 } 481 482 for _, tc := range cases { 483 actual := IsValidId(tc.Input) 484 if actual != tc.Result { 485 t.Fatalf("case: %v\tshould returned: %#v", tc, tc.Result) 486 } 487 } 488 } 489 490 func TestNowhereNil(t *testing.T) { 491 t.Parallel() 492 493 var nilStringPtr *string 494 var nonNilStringPtr *string = new(string) 495 var nilSlice []string 496 var nilStruct *struct{} 497 var nilMap map[bool]bool 498 499 var nowhereNilStruct = struct { 500 X *string 501 Y *string 502 }{ 503 nonNilStringPtr, 504 nonNilStringPtr, 505 } 506 var somewhereNilStruct = struct { 507 X *string 508 Y *string 509 }{ 510 nonNilStringPtr, 511 nilStringPtr, 512 } 513 514 var privateSomewhereNilStruct = struct { 515 X *string 516 y *string 517 }{ 518 nonNilStringPtr, 519 nilStringPtr, 520 } 521 522 testCases := []struct { 523 Description string 524 Value interface{} 525 Expected bool 526 }{ 527 { 528 "nil", 529 nil, 530 false, 531 }, 532 { 533 "empty string", 534 "", 535 true, 536 }, 537 { 538 "non-empty string", 539 "not empty!", 540 true, 541 }, 542 { 543 "nil string pointer", 544 nilStringPtr, 545 false, 546 }, 547 { 548 "non-nil string pointer", 549 nonNilStringPtr, 550 true, 551 }, 552 { 553 "0", 554 0, 555 true, 556 }, 557 { 558 "1", 559 1, 560 true, 561 }, 562 { 563 "0 (int64)", 564 int64(0), 565 true, 566 }, 567 { 568 "1 (int64)", 569 int64(1), 570 true, 571 }, 572 { 573 "true", 574 true, 575 true, 576 }, 577 { 578 "false", 579 false, 580 true, 581 }, 582 { 583 "nil slice", 584 nilSlice, 585 // A nil slice is observably the same as an empty slice, so allow it. 586 true, 587 }, 588 { 589 "empty slice", 590 []string{}, 591 true, 592 }, 593 { 594 "slice containing nils", 595 []*string{nil, nil}, 596 true, 597 }, 598 { 599 "nil map", 600 nilMap, 601 false, 602 }, 603 { 604 "non-nil map", 605 make(map[bool]bool), 606 true, 607 }, 608 { 609 "non-nil map containing nil", 610 map[bool]*string{true: nilStringPtr, false: nonNilStringPtr}, 611 // Map values are not checked 612 true, 613 }, 614 { 615 "nil struct", 616 nilStruct, 617 false, 618 }, 619 { 620 "empty struct", 621 struct{}{}, 622 true, 623 }, 624 { 625 "struct containing no nil", 626 nowhereNilStruct, 627 true, 628 }, 629 { 630 "struct containing nil", 631 somewhereNilStruct, 632 false, 633 }, 634 { 635 "struct pointer containing no nil", 636 &nowhereNilStruct, 637 true, 638 }, 639 { 640 "struct pointer containing nil", 641 &somewhereNilStruct, 642 false, 643 }, 644 { 645 "struct containing private nil", 646 privateSomewhereNilStruct, 647 true, 648 }, 649 { 650 "struct pointer containing private nil", 651 &privateSomewhereNilStruct, 652 true, 653 }, 654 } 655 656 for _, testCase := range testCases { 657 testCase := testCase 658 t.Run(testCase.Description, func(t *testing.T) { 659 defer func() { 660 if r := recover(); r != nil { 661 t.Errorf("panic: %v", r) 662 } 663 }() 664 665 t.Parallel() 666 require.Equal(t, testCase.Expected, checkNowhereNil(t, "value", testCase.Value)) 667 }) 668 } 669 } 670 671 // checkNowhereNil checks that the given interface value is not nil, and if a struct, that all of 672 // its public fields are also nowhere nil 673 func checkNowhereNil(t *testing.T, name string, value interface{}) bool { 674 if value == nil { 675 return false 676 } 677 678 v := reflect.ValueOf(value) 679 switch v.Type().Kind() { 680 case reflect.Ptr: 681 if v.IsNil() { 682 t.Logf("%s was nil", name) 683 return false 684 } 685 686 return checkNowhereNil(t, fmt.Sprintf("(*%s)", name), v.Elem().Interface()) 687 688 case reflect.Map: 689 if v.IsNil() { 690 t.Logf("%s was nil", name) 691 return false 692 } 693 694 // Don't check map values 695 return true 696 697 case reflect.Struct: 698 nowhereNil := true 699 for i := 0; i < v.NumField(); i++ { 700 f := v.Field(i) 701 // Ignore unexported fields 702 if v.Type().Field(i).PkgPath != "" { 703 continue 704 } 705 706 nowhereNil = nowhereNil && checkNowhereNil(t, fmt.Sprintf("%s.%s", name, v.Type().Field(i).Name), f.Interface()) 707 } 708 709 return nowhereNil 710 711 case reflect.Array: 712 fallthrough 713 case reflect.Chan: 714 fallthrough 715 case reflect.Func: 716 fallthrough 717 case reflect.Interface: 718 fallthrough 719 case reflect.UnsafePointer: 720 t.Logf("unhandled field %s, type: %s", name, v.Type().Kind()) 721 return false 722 723 default: 724 return true 725 } 726 }