322 lines
9.4 KiB
Go
322 lines
9.4 KiB
Go
package util_test
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"net"
|
|
"strings"
|
|
"testing"
|
|
|
|
"allaboutapps.dev/aw/go-starter/internal/util"
|
|
"github.com/go-openapi/swag"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type readInterface interface {
|
|
Read(p []byte) (n int, err error)
|
|
}
|
|
|
|
type writeInterface interface {
|
|
WriteTo(w io.Writer) (n int64, err error)
|
|
}
|
|
|
|
type testStruct struct {
|
|
// satisfy only readInterface
|
|
LimitedReader *io.LimitedReader
|
|
Reader io.Reader
|
|
|
|
// satisfy both readInterface and writeInterface
|
|
Buffer1 *bytes.Buffer
|
|
Buffer2 *bytes.Buffer
|
|
NetBuffer *net.Buffers
|
|
}
|
|
|
|
func TestGetFieldsImplementingInvalidInput(t *testing.T) {
|
|
// Invalid interfaceObject input param, must be a pointer to an interface
|
|
// Pointer to a struct
|
|
_, err := util.GetFieldsImplementing(&testStructEmpty{}, &testStructEmpty{})
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "interfaceObject")
|
|
// Pointer to a pointer to an interface
|
|
interfaceObjPtr := (*readInterface)(nil)
|
|
_, err = util.GetFieldsImplementing(&testStructEmpty{}, &interfaceObjPtr)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "interfaceObject")
|
|
|
|
// Invalid structPtr input param, must be a pointer to a struct
|
|
_, err = util.GetFieldsImplementing(testStructEmpty{}, (*readInterface)(nil))
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "structPtr")
|
|
_, err = util.GetFieldsImplementing((*readInterface)(nil), (*readInterface)(nil))
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "structPtr")
|
|
_, err = util.GetFieldsImplementing([]*testStructEmpty{}, (*readInterface)(nil))
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "structPtr")
|
|
}
|
|
|
|
func TestGetFieldsImplementingNoFields(t *testing.T) {
|
|
// No fields returned from empty structs
|
|
structEmptyFields, err := util.GetFieldsImplementing(&testStructEmpty{}, (*readInterface)(nil))
|
|
require.NoError(t, err)
|
|
assert.Empty(t, structEmptyFields)
|
|
|
|
// No fields returned from structs with only private fields
|
|
structPrivate := testStructPrivateFiled{privateMember: bytes.NewBufferString("my content")}
|
|
structPrivateFields, err := util.GetFieldsImplementing(&structPrivate, (*readInterface)(nil))
|
|
require.NoError(t, err)
|
|
assert.Empty(t, structPrivateFields)
|
|
|
|
// No fields returned if struct fields are primitive
|
|
structPrimitive := testStructPrimitives{X: 12, Y: "y", XPtr: swag.Int(15), YPtr: swag.String("YPtr")}
|
|
structPrimitiveFields, err := util.GetFieldsImplementing(&structPrimitive, (*readInterface)(nil))
|
|
require.NoError(t, err)
|
|
assert.Empty(t, structPrimitiveFields)
|
|
|
|
// No fields returned if struct fields are structs (not pointer to a struct)
|
|
structMemberStruct := testStructMemberStruct{Member: *bytes.NewBufferString("my content")}
|
|
structMemberStructFields, err := util.GetFieldsImplementing(&structMemberStruct, (*readInterface)(nil))
|
|
require.NoError(t, err)
|
|
assert.Empty(t, structMemberStructFields)
|
|
|
|
// No fieds returned if an interface is not matching
|
|
type notMatchedInterface interface {
|
|
Read(p []byte) (n int, err error, additional []string)
|
|
}
|
|
testStructObj := testStruct{}
|
|
testStructFields, err := util.GetFieldsImplementing(&testStructObj, (*notMatchedInterface)(nil))
|
|
require.NoError(t, err)
|
|
assert.Empty(t, testStructFields)
|
|
}
|
|
|
|
func TestGetFieldsImplementingMemberStructPointer(t *testing.T) {
|
|
content := "runs all day and never walks"
|
|
testStructObj := testStructMemberStructPtr{
|
|
Member: bytes.NewBufferString(content),
|
|
}
|
|
fields, err := util.GetFieldsImplementing(&testStructObj, (*readInterface)(nil))
|
|
require.NoError(t, err)
|
|
assert.Len(t, fields, 1)
|
|
|
|
output := make([]byte, len(content))
|
|
n, err := fields[0].Read(output)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, len(content), n)
|
|
assert.Equal(t, content, string(output))
|
|
}
|
|
|
|
func TestGetFieldsImplementingMemberInterface(t *testing.T) {
|
|
content := "it has a bed and never sleeps"
|
|
testStructObj := testStructMemberInterface{
|
|
Member: bytes.NewBufferString(content),
|
|
}
|
|
fields, err := util.GetFieldsImplementing(&testStructObj, (*readInterface)(nil))
|
|
require.NoError(t, err)
|
|
assert.Len(t, fields, 1)
|
|
|
|
output := make([]byte, len(content))
|
|
n, err := fields[0].Read(output)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, len(content), n)
|
|
assert.Equal(t, content, string(output))
|
|
}
|
|
|
|
func TestGetFieldsImplementingSuccess(t *testing.T) {
|
|
// Struct not initialized
|
|
// It's a responsibility of a user to make sure that the fields are not nil before using them.
|
|
structNotInitialized := testStruct{}
|
|
structNotInitializedFields, err := util.GetFieldsImplementing(&structNotInitialized, (*readInterface)(nil))
|
|
require.NoError(t, err)
|
|
// There are 4 pointer members of the testStruct satisfying the interface.
|
|
// Nil interface members are not returned.
|
|
assert.Len(t, structNotInitializedFields, 4)
|
|
for _, f := range structNotInitializedFields {
|
|
assert.Nil(t, f)
|
|
assert.Implements(t, (*readInterface)(nil), f)
|
|
}
|
|
|
|
// Struct initialized
|
|
testStructObj := testStruct{
|
|
// satisfy only readInterface
|
|
LimitedReader: &io.LimitedReader{N: 100},
|
|
Reader: strings.NewReader("did you know that"),
|
|
// satisfy both readInterface and writeInterface
|
|
Buffer1: bytes.NewBufferString("there are rats with"),
|
|
Buffer2: bytes.NewBufferString("human BRAIN cells transplanted"),
|
|
NetBuffer: &net.Buffers{[]byte{0x19}},
|
|
}
|
|
|
|
// Fields implementing readInterface
|
|
readInterfaceFields, err := util.GetFieldsImplementing(&testStructObj, (*readInterface)(nil))
|
|
require.NoError(t, err)
|
|
assert.Len(t, readInterfaceFields, 5)
|
|
|
|
for _, f := range readInterfaceFields {
|
|
assert.NotNil(t, f)
|
|
assert.Implements(t, (*readInterface)(nil), f)
|
|
}
|
|
|
|
// Fields implementing writeInterface
|
|
writeInterfaceFields, err := util.GetFieldsImplementing(&testStructObj, (*writeInterface)(nil))
|
|
require.NoError(t, err)
|
|
assert.Len(t, writeInterfaceFields, 3)
|
|
for _, f := range writeInterfaceFields {
|
|
assert.NotNil(t, f)
|
|
assert.Implements(t, (*writeInterface)(nil), f)
|
|
}
|
|
|
|
type readWriteInterface interface {
|
|
readInterface
|
|
writeInterface
|
|
}
|
|
readWriteInterfaceFields, err := util.GetFieldsImplementing(&testStructObj, (*readWriteInterface)(nil))
|
|
require.NoError(t, err)
|
|
// All members implementing writeInterface implement readInterface too
|
|
assert.Len(t, readWriteInterfaceFields, 3)
|
|
}
|
|
|
|
func TestIsStructInitialized(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
testStruct interface{}
|
|
expectError bool
|
|
errorContains []string
|
|
}{
|
|
// No error cases
|
|
{
|
|
name: "Empty struct",
|
|
testStruct: &testStructEmpty{},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Struct with initialized private field",
|
|
testStruct: &testStructPrivateFiled{privateMember: bytes.NewBufferString("my content")},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Struct with initialized primitives",
|
|
testStruct: &testStructPrimitives{
|
|
X: 1,
|
|
Y: "blabla",
|
|
XPtr: new(int),
|
|
YPtr: new(string),
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Struct with initialized maps and slices",
|
|
testStruct: &testStructMapsAndSlices{
|
|
MapMember: make(map[string]int, 0),
|
|
SliceMember: make([]int, 0),
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Struct with initialized struct member",
|
|
testStruct: &testStructMemberStruct{
|
|
Member: *bytes.NewBufferString("my content"),
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Struct with initialized struct pointer member",
|
|
testStruct: &testStructMemberStructPtr{
|
|
Member: bytes.NewBufferString("my content"),
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Struct with initialized interface member",
|
|
testStruct: &testStructMemberInterface{
|
|
Member: bytes.NewBufferString("my content"),
|
|
},
|
|
expectError: false,
|
|
},
|
|
|
|
// Error cases
|
|
{
|
|
name: "Struct with uninitialized private field",
|
|
testStruct: &testStructPrivateFiled{},
|
|
expectError: true,
|
|
errorContains: []string{"privateMember"},
|
|
},
|
|
{
|
|
name: "Struct with uninitialized primitives",
|
|
testStruct: &testStructPrimitives{},
|
|
expectError: true,
|
|
errorContains: []string{"X", "Y", "XPtr", "YPtr"},
|
|
},
|
|
{
|
|
name: "Struct with uninitialized maps and slices",
|
|
testStruct: &testStructMapsAndSlices{},
|
|
expectError: true,
|
|
errorContains: []string{"MapMember", "SliceMember"},
|
|
},
|
|
{
|
|
name: "Struct with uninitialized struct member",
|
|
testStruct: &testStructMemberStruct{},
|
|
expectError: true,
|
|
errorContains: []string{"Member"},
|
|
},
|
|
{
|
|
name: "Struct with uninitialized struct pointer member",
|
|
testStruct: &testStructMemberStructPtr{},
|
|
expectError: true,
|
|
errorContains: []string{"Member"},
|
|
},
|
|
{
|
|
name: "Struct with uninitialized interface member",
|
|
testStruct: &testStructMemberInterface{},
|
|
expectError: true,
|
|
errorContains: []string{"Member"},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := util.IsStructInitialized(tt.testStruct)
|
|
|
|
if tt.expectError {
|
|
require.Error(t, err)
|
|
for _, errText := range tt.errorContains {
|
|
assert.Contains(t, err.Error(), errText)
|
|
}
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type testStructEmpty struct {
|
|
}
|
|
|
|
type testStructPrivateFiled struct {
|
|
privateMember *bytes.Buffer
|
|
}
|
|
|
|
type testStructPrimitives struct {
|
|
X int
|
|
Y string
|
|
XPtr *int
|
|
YPtr *string
|
|
}
|
|
|
|
type testStructMapsAndSlices struct {
|
|
MapMember map[string]int
|
|
SliceMember []int
|
|
}
|
|
|
|
type testStructMemberStruct struct {
|
|
Member bytes.Buffer
|
|
}
|
|
|
|
type testStructMemberStructPtr struct {
|
|
Member *bytes.Buffer
|
|
}
|
|
|
|
type testStructMemberInterface struct {
|
|
Member io.Reader
|
|
}
|