Skip to content

Commit 064d9d0

Browse files
authored
support nested structs and pointers (#23)
* add support for pointer parameters in functions * add nested struct and pointers capability, add tests
1 parent d4fa675 commit 064d9d0

4 files changed

Lines changed: 127 additions & 4 deletions

File tree

core/runner.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func NewRunner(options *model.Options, task interface{}, parameters ...interface
3838
for i, param := range parameters {
3939
paramConverted, err := vh.AnyToType(param, taskInputParameters[i])
4040
if err != nil {
41-
return nil, fmt.Errorf("parameter %d of task must be of type %s, got %s", i, taskInputParameters[i].Kind(), reflect.TypeOf(param).Kind())
41+
return nil, fmt.Errorf("error converting parameter %d to type %s: %v", i, taskInputParameters[i].Kind(), err)
4242
}
4343
parameters[i] = paramConverted
4444
}

core/runner_test.go

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,13 @@ type TestData struct {
438438
Active bool
439439
}
440440

441+
// Nested struct that contains both a struct and pointer to struct
442+
type NestedStruct struct {
443+
Config TestConfig
444+
DataPtr *TestData
445+
Metadata string
446+
}
447+
441448
// Task function that accepts a struct and pointer to struct
442449
func taskWithStructParams(config TestConfig, data *TestData) (string, error) {
443450
if data == nil {
@@ -446,6 +453,14 @@ func taskWithStructParams(config TestConfig, data *TestData) (string, error) {
446453
return fmt.Sprintf("%s-%d: %s (ID: %d, Active: %t)", config.Name, config.Value, data.Message, data.ID, data.Active), nil
447454
}
448455

456+
// Task function that accepts a nested struct with both struct and pointer fields
457+
func taskWithNestedStruct(nested NestedStruct) (string, error) {
458+
if nested.DataPtr == nil {
459+
return "", fmt.Errorf("nested.DataPtr cannot be nil")
460+
}
461+
return fmt.Sprintf("%s: %s-%d | %s (ID: %d, Active: %t)", nested.Metadata, nested.Config.Name, nested.Config.Value, nested.DataPtr.Message, nested.DataPtr.ID, nested.DataPtr.Active), nil
462+
}
463+
449464
// TestNewRunnerWithStructParameters tests the runner with struct and pointer-to-struct parameters
450465
func TestNewRunnerWithStructParameters(t *testing.T) {
451466
tests := []struct {
@@ -560,3 +575,111 @@ func TestNewRunnerWithStructParameters(t *testing.T) {
560575
})
561576
}
562577
}
578+
579+
// TestNewRunnerWithNestedStruct tests the runner with nested structs containing both struct and pointer fields
580+
func TestNewRunnerWithNestedStruct(t *testing.T) {
581+
tests := []struct {
582+
name string
583+
param interface{}
584+
wantErr bool
585+
wantResult string
586+
}{
587+
{
588+
name: "Direct Nested Struct with Struct and Pointer",
589+
param: NestedStruct{
590+
Config: TestConfig{
591+
Name: "nested-config",
592+
Value: 100,
593+
},
594+
DataPtr: &TestData{
595+
ID: 10,
596+
Message: "nested data",
597+
Active: true,
598+
},
599+
Metadata: "meta-direct",
600+
},
601+
wantErr: false,
602+
wantResult: "meta-direct: nested-config-100 | nested data (ID: 10, Active: true)",
603+
},
604+
{
605+
name: "Map to Nested Struct Conversion",
606+
param: map[string]interface{}{
607+
"Config": map[string]interface{}{
608+
"Name": "map-config",
609+
"Value": 200,
610+
},
611+
"DataPtr": map[string]interface{}{
612+
"ID": 20,
613+
"Message": "map data",
614+
"Active": false,
615+
},
616+
"Metadata": "meta-from-map",
617+
},
618+
wantErr: false,
619+
wantResult: "meta-from-map: map-config-200 | map data (ID: 20, Active: false)",
620+
},
621+
{
622+
name: "Map with Type Conversions in Nested Struct",
623+
param: map[string]interface{}{
624+
"Config": map[string]interface{}{
625+
"Name": "type-conv",
626+
"Value": 300.5, // float64 to int
627+
},
628+
"DataPtr": map[string]interface{}{
629+
"ID": 30.0, // float64 to int
630+
"Message": "type conversion",
631+
"Active": true,
632+
},
633+
"Metadata": "meta-type-conv",
634+
},
635+
wantErr: false,
636+
wantResult: "meta-type-conv: type-conv-300 | type conversion (ID: 30, Active: true)",
637+
},
638+
}
639+
640+
for _, test := range tests {
641+
t.Run(test.name, func(t *testing.T) {
642+
mockTask, err := model.NewTask(taskWithNestedStruct)
643+
require.NoError(t, err, "Failed to create mock task")
644+
645+
job := &model.Job{
646+
TaskName: mockTask.Name,
647+
Parameters: []interface{}{
648+
test.param,
649+
},
650+
Options: &model.Options{
651+
OnError: &model.OnError{
652+
Timeout: 5.0,
653+
},
654+
},
655+
}
656+
657+
runner, err := NewRunnerFromJob(mockTask, job)
658+
if test.wantErr {
659+
assert.Error(t, err, "NewRunnerFromJob should return an error")
660+
return
661+
}
662+
663+
require.NoError(t, err, "NewRunnerFromJob should not return an error")
664+
assert.NotNil(t, runner)
665+
666+
go runner.Run(context.Background())
667+
668+
outerLoop:
669+
for {
670+
select {
671+
case err := <-runner.ErrorChannel:
672+
assert.NoError(t, err, "Runner should not return an error")
673+
break outerLoop
674+
case results := <-runner.ResultsChannel:
675+
assert.NotNil(t, results)
676+
assert.Len(t, results, 1)
677+
assert.Equal(t, test.wantResult, results[0])
678+
break outerLoop
679+
case <-time.After(2 * time.Second):
680+
t.Fatal("Test timed out")
681+
}
682+
}
683+
})
684+
}
685+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ require (
6868
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
6969
github.com/shirou/gopsutil/v4 v4.25.10 // indirect
7070
github.com/siherrmann/queuerSql v0.0.0-20251130135331-9ed23b19fae5
71-
github.com/siherrmann/validator v0.11.0
71+
github.com/siherrmann/validator v0.12.0
7272
github.com/sirupsen/logrus v1.9.3 // indirect
7373
github.com/spf13/cobra v1.10.1
7474
github.com/tklauser/go-sysconf v0.3.16 // indirect

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,8 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7
127127
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
128128
github.com/shirou/gopsutil/v4 v4.25.10 h1:at8lk/5T1OgtuCp+AwrDofFRjnvosn0nkN2OLQ6g8tA=
129129
github.com/shirou/gopsutil/v4 v4.25.10/go.mod h1:+kSwyC8DRUD9XXEHCAFjK+0nuArFJM0lva+StQAcskM=
130-
github.com/siherrmann/validator v0.11.0 h1:DTLzGvwZUJwhr842xZkX/m7ed4DMFKJ9AKoYDk4+cNs=
131-
github.com/siherrmann/validator v0.11.0/go.mod h1:LqNNViIg5YKzO4SX4zMdafyffL6xCWuRqSyt1szpolo=
130+
github.com/siherrmann/validator v0.12.0 h1:bW80s6cXVuqIYPWCB8nboXryJWVog1C+XOQW72XovbE=
131+
github.com/siherrmann/validator v0.12.0/go.mod h1:LqNNViIg5YKzO4SX4zMdafyffL6xCWuRqSyt1szpolo=
132132
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
133133
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
134134
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=

0 commit comments

Comments
 (0)