Skip to content

Commit

Permalink
test: slice of structs (#15)
Browse files Browse the repository at this point in the history
* test: slice of structs
* fix: use github.com/flanksource/mapstructure
  • Loading branch information
adityathebe authored Jul 24, 2023
1 parent ee41d23 commit 765b014
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 25 deletions.
2 changes: 1 addition & 1 deletion cmd/gencel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
)

// Not really a test but just a runner so it's easier to attach a debugger.
func TestGencel(t *testing.T) {
func testGencel(t *testing.T) {
wd, _ := os.Getwd()
fmt.Printf("WD: %s", wd)

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ require (
github.com/Masterminds/goutils v1.1.1
github.com/Masterminds/semver/v3 v3.2.1
github.com/flanksource/is-healthy v0.0.0-20230705092916-3b4cf510c5fc
github.com/flanksource/mapstructure v1.6.0
github.com/google/cel-go v0.17.1
github.com/google/uuid v1.3.0
github.com/gosimple/slug v1.13.1
github.com/hairyhenderson/toml v0.4.2-0.20210923231440-40456b8e66cf
github.com/itchyny/gojq v0.12.13
github.com/mitchellh/mapstructure v1.5.0
github.com/pkg/errors v0.9.1
github.com/robertkrimen/otto v0.2.1
github.com/stretchr/testify v1.8.4
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/flanksource/is-healthy v0.0.0-20230705092916-3b4cf510c5fc h1:CPUNUw2pHnlF4ucBHx44vLTcCa4FlEEu6PkNo5rCvD4=
github.com/flanksource/is-healthy v0.0.0-20230705092916-3b4cf510c5fc/go.mod h1:4pQhmF+TnVqJroQKY8wSnSp+T18oLson6YQ2M0qPHfQ=
github.com/flanksource/mapstructure v1.6.0 h1:+1kJ+QsO1SxjAgktfLlpZXetsVSJ0uCLhGKrA4BtwTE=
github.com/flanksource/mapstructure v1.6.0/go.mod h1:dttg5+FFE2sp4D/CrcPCVqufNDrBggDaM+08nk5S8Ps=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
Expand Down Expand Up @@ -44,8 +46,6 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
Expand Down
52 changes: 37 additions & 15 deletions template.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import (
"github.com/flanksource/gomplate/v3/funcs"
_ "github.com/flanksource/gomplate/v3/js"
pkgStrings "github.com/flanksource/gomplate/v3/strings"
"github.com/flanksource/mapstructure"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/ext"
"github.com/mitchellh/mapstructure"
"github.com/robertkrimen/otto"
"github.com/robertkrimen/otto/registry"
_ "github.com/robertkrimen/otto/underscore"
Expand Down Expand Up @@ -114,7 +114,7 @@ func RunTemplate(environment map[string]any, template Template) (string, error)

out, _, err := prg.Eval(data)
if err != nil {
return "", err
return "", fmt.Errorf("error evaluating expression %s: %v", template.Expression, err)
}

return fmt.Sprintf("%v", out.Value()), nil
Expand Down Expand Up @@ -145,22 +145,44 @@ func serialize(in map[string]any) (map[string]any, error) {

newMap := make(map[string]any, len(in))
for k, v := range in {
if reflect.ValueOf(v).Kind() != reflect.Struct {
newMap[k] = v
continue
}
var dec *mapstructure.Decoder
var err error

vt := reflect.TypeOf(v)
switch vt.Kind() {
case reflect.Struct:
var result map[string]any
dec, err = mapstructure.NewDecoder(&mapstructure.DecoderConfig{TagName: "json", Result: &result, Squash: true, Deep: true})
if err != nil {
return nil, fmt.Errorf("error creating new mapstructure decoder: %w", err)
}

var vMap map[string]any
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{TagName: "json", Result: &vMap, Squash: true})
if err != nil {
return nil, fmt.Errorf("error creating new mapstructure decoder: %w", err)
}
if err := dec.Decode(v); err != nil {
return nil, fmt.Errorf("error decoding %T to map[string]any: %w", v, err)
}

if err := dec.Decode(v); err != nil {
return nil, fmt.Errorf("error decoding %T to map[string]any: %w", v, err)
}
newMap[k] = result

newMap[k] = vMap
case reflect.Slice:
var result any
if vt.Elem().Kind() == reflect.Struct {
result = make([]map[string]any, 0)
}

dec, err = mapstructure.NewDecoder(&mapstructure.DecoderConfig{TagName: "json", Result: &result, Squash: true, Deep: true})
if err != nil {
return nil, fmt.Errorf("error creating new mapstructure decoder: %w", err)
}
if err := dec.Decode(v); err != nil {
return nil, fmt.Errorf("error decoding %T to map[string]any: %w", v, err)
}

newMap[k] = result

default:
newMap[k] = v
continue
}
}

return newMap, nil
Expand Down
100 changes: 94 additions & 6 deletions template_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gomplate

import (
"reflect"
"testing"
"time"

Expand All @@ -10,22 +11,28 @@ import (
"github.com/stretchr/testify/assert"
)

type NoStructTag struct {
Name string
UPPER string
}

type Address struct {
City string `json:"city_name"`
}

type Person struct {
Name string `json:"name"`
Address Address
MetaData map[string]any
Codes []string
Name string `json:"name"`
Address *Address `json:",omitempty"`
MetaData map[string]any `json:",omitempty"`
Codes []string `json:",omitempty"`
Addresses []Address `json:"addresses,omitempty"`
}

// A shared test data for all template test
var structEnv = map[string]any{
"results": Person{
Name: "Aditya",
Address: Address{
Address: &Address{
City: "Kathmandu",
},
},
Expand Down Expand Up @@ -58,6 +65,7 @@ var junitEnv = JunitTestSuites{
Totals: Totals{
Passed: 1,
},
Suites: []JunitTestSuite{{Name: "hi", Totals: Totals{Failed: 2}}},
}

type SQLDetails struct {
Expand Down Expand Up @@ -112,7 +120,11 @@ func TestGomplate(t *testing.T) {
{map[string]interface{}{"old": "1.2.3", "new": "1.2.3"}, "{{ .old | semverCompare .new }}", "true"},
{map[string]interface{}{"old": "1.2.3", "new": "1.2.4"}, "{{ .old | semverCompare .new }}", "false"},
{structEnv, `{{.results.name}} {{.results.Address.city_name}}`, "Aditya Kathmandu"},
{map[string]any{"results": junitEnv}, `{{.results.passed}}`, "1"},
{
map[string]any{"results": junitEnv},
`{{.results.passed}}{{ range $r := .results.suites}}{{$r.name}} ✅ {{$r.passed}} ❌ {{$r.failed}} in 🕑 {{$r.duration}}{{end}}`,
"1hi ✅ 0 ❌ 2 in 🕑 0",
},
{
map[string]any{
"results": SQLDetails{
Expand Down Expand Up @@ -184,3 +196,79 @@ func TestCel(t *testing.T) {
})
}
}

func Test_serialize(t *testing.T) {
tests := []struct {
name string
in map[string]any
want map[string]any
wantErr bool
}{
{name: "nil", in: nil, want: nil, wantErr: false},
{name: "empty", in: map[string]any{}, want: map[string]any{}, wantErr: false},
{
name: "simple - no struct tags",
in: map[string]any{"r": NoStructTag{Name: "Kathmandu", UPPER: "u"}},
want: map[string]any{"r": map[string]any{"Name": "Kathmandu", "UPPER": "u"}},
wantErr: false,
},
{name: "simple - struct tags", in: map[string]any{"r": Address{City: "Kathmandu"}}, want: map[string]any{"r": map[string]any{"city_name": "Kathmandu"}}, wantErr: false},
{
name: "nested struct",
in: map[string]any{"r": Person{Name: "Aditya", Address: &Address{City: "Kathmandu"}}},
want: map[string]any{"r": map[string]any{"name": "Aditya", "Address": map[string]any{"city_name": "Kathmandu"}}},
wantErr: false,
},
{
name: "slice of struct",
in: map[string]any{
"r": []Address{
{City: "Kathmandu"},
{City: "Lalitpur"},
},
},
want: map[string]any{
"r": []map[string]any{
{"city_name": "Kathmandu"},
{"city_name": "Lalitpur"},
},
},
wantErr: false,
},
{
name: "nested slice of struct",
in: map[string]any{
"r": Person{
Name: "Aditya",
Addresses: []Address{
{City: "Kathmandu"},
{City: "Lalitpur"},
},
},
},
want: map[string]any{
"r": map[string]any{
"name": "Aditya",
"addresses": []map[string]any{
{"city_name": "Kathmandu"},
{"city_name": "Lalitpur"},
},
},
},
wantErr: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := serialize(tt.in)
if (err != nil) != tt.wantErr {
t.Errorf("serialize() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("serialize() = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit 765b014

Please sign in to comment.