Full Mattermost server source with integrated Community Enterprise features. Includes vendor directory for offline/air-gapped builds. Structure: - enterprise-impl/: Enterprise feature implementations - enterprise-community/: Init files that register implementations - enterprise/: Bridge imports (community_imports.go) - vendor/: All dependencies for offline builds Build (online): go build ./cmd/mattermost Build (offline/air-gapped): go build -mod=vendor ./cmd/mattermost 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
354 lines
7.3 KiB
Go
354 lines
7.3 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package utils_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
"github.com/mattermost/mattermost/server/public/utils"
|
|
)
|
|
|
|
func TestHumanizeJsonError(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
type testType struct{}
|
|
|
|
testCases := []struct {
|
|
Description string
|
|
Data []byte
|
|
Err error
|
|
ExpectedErr string
|
|
}{
|
|
{
|
|
"nil error",
|
|
[]byte{},
|
|
nil,
|
|
"",
|
|
},
|
|
{
|
|
"non-special error",
|
|
[]byte{},
|
|
errors.New("test"),
|
|
"test",
|
|
},
|
|
{
|
|
"syntax error, offset 17, middle of line 3",
|
|
[]byte("line 1\nline 2\nline 3"),
|
|
&json.SyntaxError{
|
|
// msg can't be set
|
|
Offset: 17,
|
|
},
|
|
"parsing error at line 3, character 4: ",
|
|
},
|
|
{
|
|
"unmarshal type error, offset 17, middle of line 3",
|
|
[]byte("line 1\nline 2\nline 3"),
|
|
&json.UnmarshalTypeError{
|
|
Value: "bool",
|
|
Type: reflect.TypeFor[testType](),
|
|
Offset: 17,
|
|
Struct: "struct",
|
|
Field: "field",
|
|
},
|
|
"parsing error at line 3, character 4: json: cannot unmarshal bool into Go struct field struct.field of type utils_test.testType",
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.Description, func(t *testing.T) {
|
|
actual := utils.HumanizeJSONError(testCase.Err, testCase.Data)
|
|
if testCase.ExpectedErr == "" {
|
|
assert.NoError(t, actual)
|
|
} else {
|
|
assert.EqualError(t, actual, testCase.ExpectedErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewHumanizedJSONError(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testCases := []struct {
|
|
Description string
|
|
Data []byte
|
|
Offset int64
|
|
Err error
|
|
Expected *utils.HumanizedJSONError
|
|
}{
|
|
{
|
|
"nil error",
|
|
[]byte{},
|
|
0,
|
|
nil,
|
|
nil,
|
|
},
|
|
{
|
|
"offset -1, before start of string",
|
|
[]byte("line 1\nline 2\nline 3"),
|
|
-1,
|
|
errors.New("message"),
|
|
&utils.HumanizedJSONError{
|
|
Err: errors.Wrap(errors.New("message"), "invalid offset -1"),
|
|
},
|
|
},
|
|
{
|
|
"offset 0, start of string",
|
|
[]byte("line 1\nline 2\nline 3"),
|
|
0,
|
|
errors.New("message"),
|
|
&utils.HumanizedJSONError{
|
|
Err: errors.Wrap(errors.New("message"), "parsing error at line 1, character 1"),
|
|
Line: 1,
|
|
Character: 1,
|
|
},
|
|
},
|
|
{
|
|
"offset 5, end of line 1",
|
|
[]byte("line 1\nline 2\nline 3"),
|
|
5,
|
|
errors.New("message"),
|
|
&utils.HumanizedJSONError{
|
|
Err: errors.Wrap(errors.New("message"), "parsing error at line 1, character 6"),
|
|
Line: 1,
|
|
Character: 6,
|
|
},
|
|
},
|
|
{
|
|
"offset 6, new line at end end of line 1",
|
|
[]byte("line 1\nline 2\nline 3"),
|
|
6,
|
|
errors.New("message"),
|
|
&utils.HumanizedJSONError{
|
|
Err: errors.Wrap(errors.New("message"), "parsing error at line 1, character 7"),
|
|
Line: 1,
|
|
Character: 7,
|
|
},
|
|
},
|
|
{
|
|
"offset 7, start of line 2",
|
|
[]byte("line 1\nline 2\nline 3"),
|
|
7,
|
|
errors.New("message"),
|
|
&utils.HumanizedJSONError{
|
|
Err: errors.Wrap(errors.New("message"), "parsing error at line 2, character 1"),
|
|
Line: 2,
|
|
Character: 1,
|
|
},
|
|
},
|
|
{
|
|
"offset 12, end of line 2",
|
|
[]byte("line 1\nline 2\nline 3"),
|
|
12,
|
|
errors.New("message"),
|
|
&utils.HumanizedJSONError{
|
|
Err: errors.Wrap(errors.New("message"), "parsing error at line 2, character 6"),
|
|
Line: 2,
|
|
Character: 6,
|
|
},
|
|
},
|
|
{
|
|
"offset 13, newline at end of line 2",
|
|
[]byte("line 1\nline 2\nline 3"),
|
|
13,
|
|
errors.New("message"),
|
|
&utils.HumanizedJSONError{
|
|
Err: errors.Wrap(errors.New("message"), "parsing error at line 2, character 7"),
|
|
Line: 2,
|
|
Character: 7,
|
|
},
|
|
},
|
|
{
|
|
"offset 17, middle of line 3",
|
|
[]byte("line 1\nline 2\nline 3"),
|
|
17,
|
|
errors.New("message"),
|
|
&utils.HumanizedJSONError{
|
|
Err: errors.Wrap(errors.New("message"), "parsing error at line 3, character 4"),
|
|
Line: 3,
|
|
Character: 4,
|
|
},
|
|
},
|
|
{
|
|
"offset 19, end of string",
|
|
[]byte("line 1\nline 2\nline 3"),
|
|
19,
|
|
errors.New("message"),
|
|
&utils.HumanizedJSONError{
|
|
Err: errors.Wrap(errors.New("message"), "parsing error at line 3, character 6"),
|
|
Line: 3,
|
|
Character: 6,
|
|
},
|
|
},
|
|
{
|
|
"offset 20, offset = length of string",
|
|
[]byte("line 1\nline 2\nline 3"),
|
|
20,
|
|
errors.New("message"),
|
|
&utils.HumanizedJSONError{
|
|
Err: errors.Wrap(errors.New("message"), "parsing error at line 3, character 7"),
|
|
Line: 3,
|
|
Character: 7,
|
|
},
|
|
},
|
|
{
|
|
"offset 21, offset = length of string, after newline",
|
|
[]byte("line 1\nline 2\nline 3\n"),
|
|
21,
|
|
errors.New("message"),
|
|
&utils.HumanizedJSONError{
|
|
Err: errors.Wrap(errors.New("message"), "parsing error at line 4, character 1"),
|
|
Line: 4,
|
|
Character: 1,
|
|
},
|
|
},
|
|
{
|
|
"offset 21, offset > length of string",
|
|
[]byte("line 1\nline 2\nline 3"),
|
|
21,
|
|
errors.New("message"),
|
|
&utils.HumanizedJSONError{
|
|
Err: errors.Wrap(errors.New("message"), "invalid offset 21"),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.Description, func(t *testing.T) {
|
|
actual := utils.NewHumanizedJSONError(testCase.Err, testCase.Data, testCase.Offset)
|
|
if testCase.Expected != nil && actual.Err != nil {
|
|
if assert.EqualValues(t, testCase.Expected.Err.Error(), actual.Err.Error()) {
|
|
actual.Err = testCase.Expected.Err
|
|
}
|
|
}
|
|
assert.Equal(t, testCase.Expected, actual)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIsJSONEmpty(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testCases := []struct {
|
|
Description string
|
|
Data []byte
|
|
Empty bool
|
|
}{
|
|
{
|
|
"nil []byte is empty",
|
|
nil,
|
|
true,
|
|
},
|
|
{
|
|
"Zero length slice is empty",
|
|
[]byte(""),
|
|
true,
|
|
},
|
|
{
|
|
"braces are empty",
|
|
[]byte("{}"),
|
|
true,
|
|
},
|
|
{
|
|
"square brackets are empty",
|
|
[]byte("[]"),
|
|
true,
|
|
},
|
|
{
|
|
"empty string is empty",
|
|
[]byte("\"\""),
|
|
true,
|
|
},
|
|
{
|
|
"map is not empty",
|
|
[]byte("{\"foo\":7}"),
|
|
false,
|
|
},
|
|
{
|
|
"array is not empty",
|
|
[]byte("[1,2,3]"),
|
|
false,
|
|
},
|
|
{
|
|
"string is not empty",
|
|
[]byte("\"hello\""),
|
|
false,
|
|
},
|
|
{
|
|
"whitespace still empty",
|
|
[]byte(" \n { \t } "),
|
|
true,
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.Description, func(t *testing.T) {
|
|
empty := utils.IsEmptyJSON(testCase.Data)
|
|
assert.Equal(t, testCase.Empty, empty)
|
|
|
|
if !testCase.Empty {
|
|
// don't really need to test the JSON unmarshaller but this is included
|
|
// to ensure the test cases stay valid.
|
|
var v any
|
|
err := json.Unmarshal(testCase.Data, &v)
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStringPtrToJSON(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testCases := []struct {
|
|
Description string
|
|
Ptr *string
|
|
Expect json.RawMessage
|
|
}{
|
|
{
|
|
"nil string ptr",
|
|
nil,
|
|
[]byte("{}"),
|
|
},
|
|
{
|
|
"Zero length string",
|
|
model.NewPointer(""),
|
|
[]byte("{}"),
|
|
},
|
|
{
|
|
"JSON map",
|
|
model.NewPointer("{\"foo\":7}"),
|
|
[]byte("{\"foo\":7}"),
|
|
},
|
|
{
|
|
"JSON array",
|
|
model.NewPointer("[1,2,3]"),
|
|
[]byte("[1,2,3]"),
|
|
},
|
|
{
|
|
"JSON string",
|
|
model.NewPointer("\"hello\""),
|
|
[]byte("\"hello\""),
|
|
},
|
|
{
|
|
"bare string",
|
|
model.NewPointer("hello"),
|
|
[]byte("\"hello\""),
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.Description, func(t *testing.T) {
|
|
j := utils.StringPtrToJSON(testCase.Ptr)
|
|
assert.Equal(t, testCase.Expect, j)
|
|
})
|
|
}
|
|
}
|