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  }