(Feat): Initial Commit
This commit is contained in:
321
internal/util/struct_test.go
Normal file
321
internal/util/struct_test.go
Normal file
@@ -0,0 +1,321 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user