Files
Gumble-Backend/internal/util/struct_test.go
Zane Walker 7e940c83a7
Some checks failed
Build & Test / build-test (push) Has been cancelled
Build & Test / swagger-codegen-cli (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled
(Feat): Initial Commit
2026-07-03 19:41:31 +05:30

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
}