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  }