Files
temporal/chasm/fields_iterator_test.go
Stephan Behnke 3bc2ffb276 Apply lint fixes for oss-foundations (#10796)
## What changed?

- Applied testifylint, staticcheck, and gofix auto-fixes.
- Exact commands that were run:

```sh
.bin/golangci-lint-v2.9.0 run --allow-parallel-runners --concurrency 4 --fix --enable-only testifylint --build-tags disable_grpc_modules,test_dep --timeout 20m --config=.github/.golangci.yml
.bin/golangci-lint-v2.9.0 run --allow-parallel-runners --concurrency 4 --fix --enable-only staticcheck --build-tags disable_grpc_modules,test_dep --timeout 20m --config=.github/.golangci.yml
make fmt-gofix
make goimports
make fmt
git diff --check
```

- No manual or AI changes were made; except where commented on.
- Some fixes caused lint errors; those were reverted again.
- Changes were all reviewed by me.
2026-06-24 11:04:58 -07:00

244 lines
6.8 KiB
Go

package chasm
import (
"reflect"
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"go.uber.org/mock/gomock"
)
type fieldsIteratorSuite struct {
suite.Suite
*require.Assertions
controller *gomock.Controller
}
func TestFieldsIteratorSuite(t *testing.T) {
suite.Run(t, new(fieldsIteratorSuite))
}
func (s *fieldsIteratorSuite) SetupTest() {
s.initAssertions()
s.controller = gomock.NewController(s.T())
}
func (s *fieldsIteratorSuite) SetupSubTest() {
s.initAssertions()
}
func (s *fieldsIteratorSuite) initAssertions() {
// `s.Assertions` (as well as other test helpers which depends on `s.T()`) must be initialized on
// both test and subtest levels (but not suite level, where `s.T()` is `nil`).
//
// If these helpers are not reinitialized on subtest level, any failed `assert` in
// subtest will fail the entire test (not subtest) immediately without running other subtests.
s.Assertions = require.New(s.T())
}
func (s *fieldsIteratorSuite) TestGenericTypePrefix() {
tests := []struct {
name string
input any
expected string
}{
{
name: "Field type",
input: Field[string]{},
expected: chasmFieldTypePrefix,
},
{
name: "Map type",
input: Map[string, int]{},
expected: chasmMapTypePrefix,
},
{
name: "Non-generic type",
input: 0,
expected: "",
},
{
name: "Map type",
input: map[string]int{},
expected: "map[",
},
}
for _, tt := range tests {
s.Run(tt.name, func() {
typ := reflect.TypeOf(tt.input)
result := genericTypePrefix(typ)
s.Equal(tt.expected, result)
})
}
}
func (s *fieldsIteratorSuite) TestChasmFieldTypePrefix() {
fT := reflect.TypeFor[Field[any]]()
s.True(strings.HasPrefix(fT.String(), chasmFieldTypePrefix))
}
func (s *fieldsIteratorSuite) TestChasmMapTypePrefix() {
cT := reflect.TypeFor[Map[string, any]]()
s.True(strings.HasPrefix(cT.String(), chasmMapTypePrefix))
}
func (s *fieldsIteratorSuite) TestFieldsOf() {
type fieldPointer struct {
DataField *protoMessageType
InvalidField *Field[string]
}
type noDataField struct {
SubField Field[string]
SubMap Map[string, int]
}
type twoDataFields struct {
DataField *protoMessageType
AnotherDataField *protoMessageType
}
type unimplementedComponentOnly struct {
UnimplementedComponent
}
tests := []struct {
name string
input any
expectedKinds []fieldKind
expectedNames []string
expectedTypes []string
expectedErrors []string
}{
{
name: "Valid component with one data field",
input: &struct {
UnimplementedComponent
DataField *protoMessageType
SubField Field[string]
SubMap Map[string, int]
ignored *struct{}
}{},
expectedKinds: []fieldKind{fieldKindData, fieldKindSubField, fieldKindSubMap},
expectedNames: []string{"DataField", "SubField", "SubMap"},
expectedTypes: []string{"*persistence.WorkflowExecutionState", "chasm.Field[string]", "chasm.Map[string,int]"},
expectedErrors: []string{"", "", ""},
},
{
name: "Component with no data field",
input: &noDataField{},
expectedKinds: []fieldKind{fieldKindSubField, fieldKindSubMap, fieldKindUnspecified},
expectedNames: []string{"SubField", "SubMap", ""},
expectedTypes: []string{"chasm.Field[string]", "chasm.Map[string,int]", ""},
expectedErrors: []string{"", "", "*chasm.noDataField: no data field (implements proto.Message) found"},
},
{
name: "Component with *Field",
input: &fieldPointer{},
expectedKinds: []fieldKind{fieldKindData, fieldKindUnspecified},
expectedNames: []string{"DataField", "InvalidField"},
expectedTypes: []string{"*persistence.WorkflowExecutionState", "*chasm.Field[string]"},
expectedErrors: []string{"", "*chasm.fieldPointer.InvalidField: CHASM fields must not be pointers"},
},
{
name: "Component with multiple data fields",
input: &twoDataFields{},
expectedKinds: []fieldKind{fieldKindData, fieldKindData},
expectedNames: []string{"DataField", "AnotherDataField"},
expectedTypes: []string{"*persistence.WorkflowExecutionState", "*persistence.WorkflowExecutionState"},
expectedErrors: []string{"", "*chasm.twoDataFields.AnotherDataField: only one data field DataField (implements proto.Message) allowed in component"},
},
{
name: "Component with UnimplementedComponent only",
input: &unimplementedComponentOnly{},
expectedKinds: []fieldKind{fieldKindUnspecified},
expectedNames: []string{""},
expectedTypes: []string{""},
expectedErrors: []string{"*chasm.unimplementedComponentOnly: no data field (implements proto.Message) found"},
},
}
for _, tt := range tests {
s.Run(tt.name, func() {
valueV := reflect.ValueOf(tt.input)
var actualKinds []fieldKind
var actualNames []string
var actualTypes []string
var actualErrors []string
for field := range fieldsOf(valueV) {
actualKinds = append(actualKinds, field.kind)
actualNames = append(actualNames, field.name)
if field.typ != nil {
actualTypes = append(actualTypes, field.typ.String())
} else {
actualTypes = append(actualTypes, "")
}
if field.err != nil {
actualErrors = append(actualErrors, field.err.Error())
} else {
actualErrors = append(actualErrors, "")
}
}
s.Equal(tt.expectedKinds, actualKinds)
s.Equal(tt.expectedNames, actualNames)
s.Equal(tt.expectedTypes, actualTypes)
s.Equal(tt.expectedErrors, actualErrors)
})
}
}
func (s *fieldsIteratorSuite) TestUnmanagedFieldsOf() {
type unmanagedFields struct {
UnimplementedComponent
DataField *protoMessageType
SomeField Field[string]
unmanaged struct{}
anotherPtr *struct{}
}
var result []string
for r := range unmanagedFieldsOf(reflect.TypeFor[unmanagedFields]()) {
result = append(result, r.name)
}
s.Len(result, 2)
s.ElementsMatch([]string{"unmanaged", "anotherPtr"}, result)
}
func (s *fieldsIteratorSuite) TestHasVisibilityField() {
type componentWithVisibility struct {
UnimplementedComponent
DataField *protoMessageType
Visibility Field[*Visibility]
}
type componentWithoutVisibility struct {
UnimplementedComponent
DataField *protoMessageType
SomeField Field[string]
}
s.Run("component with Visibility field", func() {
s.True(hasVisibilityField(reflect.TypeFor[componentWithVisibility]()))
s.True(hasVisibilityField(reflect.TypeFor[*componentWithVisibility]()))
})
s.Run("component without Visibility field", func() {
s.False(hasVisibilityField(reflect.TypeFor[componentWithoutVisibility]()))
s.False(hasVisibilityField(reflect.TypeFor[*componentWithoutVisibility]()))
})
s.Run("non-struct type", func() {
s.False(hasVisibilityField(reflect.TypeFor[string]()))
s.False(hasVisibilityField(reflect.TypeFor[int]()))
})
}