mirror of
https://github.com/mattermost/mattermost.git
synced 2026-05-27 20:26:11 -04:00
* MM-68149: upgrade to Go 1.26.2 Update go directive in go.mod and .go-version. * MM-68149: replace pointer helpers with Go 1.26 new() Go 1.26 extends the built-in new() to accept an initial value expression, making typed-pointer helpers like model.NewPointer(x), bToP(x), and boolPtr(x) redundant. Replace every call site with new(x) and remove the now-unused helper functions and their //go:fix inline directives. * MM-68149: apply go fix for reflect API and format-string changes - reflect.Ptr → reflect.Pointer (renamed in Go 1.18, deprecated alias removed in 1.26) - reflect range-over-struct: for i := 0; i < t.NumField(); i++ → for field := range t.Fields() and the equivalent for Methods() and interface types - Fix format-string concatenation and variadic-arg mismatches flagged by go vet * MM-68149: update JPEG fixtures and test infrastructure for Go 1.26 encoder Go 1.26 ships a new image/jpeg encoder that produces slightly different output. Regenerate all JPEG fixture files and switch the comparison helpers from byte-equality to pixel-level comparison with a small per-channel tolerance, so minor encoder drift across patch versions is handled automatically. Add -update-fixtures flag to make it easy to regenerate fixtures after future major Go upgrades. Document the update procedure in tests/README.md. * MM-68149: CI check that go fix ./... produces no changes * Fix real bugs flagged by CodeRabbit review - group.go: set newGroup.MemberCount not group.MemberCount (member count was populated on the wrong variable and lost before publish/return) - file_test.go: guard compareImage(GetFilePreview) on the preview slice length, not the thumbnail slice length (copy-paste error) - config_test.go: remove duplicate MinimumLength assignment * fixup! Fix real bugs flagged by CodeRabbit review
352 lines
7.2 KiB
Go
352 lines
7.2 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/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",
|
|
new(""),
|
|
[]byte("{}"),
|
|
},
|
|
{
|
|
"JSON map",
|
|
new("{\"foo\":7}"),
|
|
[]byte("{\"foo\":7}"),
|
|
},
|
|
{
|
|
"JSON array",
|
|
new("[1,2,3]"),
|
|
[]byte("[1,2,3]"),
|
|
},
|
|
{
|
|
"JSON string",
|
|
new("\"hello\""),
|
|
[]byte("\"hello\""),
|
|
},
|
|
{
|
|
"bare string",
|
|
new("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)
|
|
})
|
|
}
|
|
}
|