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  }