package api import ( "encoding/json" "fmt" "os" "strings" "testing" "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/iostreams" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func Test_parseFields(t *testing.T) { ios, stdin, _, _ := iostreams.Test() fmt.Fprint(stdin, "pasted contents") opts := ApiOptions{ IO: ios, RawFields: []string{ "robot=Hubot", "destroyer=false", "helper=true", "location=@work", }, MagicFields: []string{ "input=@-", "enabled=true", "victories=123", }, } params, err := parseFields(&opts) if err != nil { t.Fatalf("parseFields error: %v", err) } expect := map[string]interface{}{ "robot": "Hubot", "destroyer": "false", "helper": "true", "location": "@work", "input": "pasted contents", "enabled": true, "victories": 123, } assert.Equal(t, expect, params) } func Test_parseFields_nested(t *testing.T) { ios, stdin, _, _ := iostreams.Test() fmt.Fprint(stdin, "pasted contents") opts := ApiOptions{ IO: ios, RawFields: []string{ "branch[name]=patch-1", "robots[]=Hubot", "robots[]=Dependabot", "labels[][name]=bug", "labels[][color]=red", "labels[][colorOptions][]=red", "labels[][colorOptions][]=blue", "labels[][name]=feature", "labels[][color]=green", "labels[][colorOptions][]=red", "labels[][colorOptions][]=green", "labels[][colorOptions][]=yellow", "nested[][key1][key2][key3]=value", "empty[]", }, MagicFields: []string{ "branch[protections]=true", "ids[]=123", "ids[]=456", }, } params, err := parseFields(&opts) if err != nil { t.Fatalf("parseFields error: %v", err) } jsonData, err := json.MarshalIndent(params, "", "\t") if err != nil { t.Fatal(err) } assert.Equal(t, strings.TrimSuffix(heredoc.Doc(` { "branch": { "name": "patch-1", "protections": true }, "empty": [], "ids": [ 123, 456 ], "labels": [ { "color": "red", "colorOptions": [ "red", "blue" ], "name": "bug" }, { "color": "green", "colorOptions": [ "red", "green", "yellow" ], "name": "feature" } ], "nested": [ { "key1": { "key2": { "key3": "value" } } } ], "robots": [ "Hubot", "Dependabot" ] } `), "\n"), string(jsonData)) } func Test_parseFields_errors(t *testing.T) { ios, stdin, _, _ := iostreams.Test() fmt.Fprint(stdin, "pasted contents") tests := []struct { name string opts *ApiOptions expected string }{ { name: "cannot overwrite string to array", opts: &ApiOptions{ IO: ios, RawFields: []string{ "object[field]=A", "object[field][]=this should be an error", }, }, expected: `expected array type under "field", got string`, }, { name: "cannot overwrite string to object", opts: &ApiOptions{ IO: ios, RawFields: []string{ "object[field]=B", "object[field][field2]=this should be an error", }, }, expected: `expected map type under "field", got string`, }, { name: "cannot overwrite object to string", opts: &ApiOptions{ IO: ios, RawFields: []string{ "object[field][field2]=C", "object[field]=this should be an error", }, }, expected: `unexpected override existing field under "field"`, }, { name: "cannot overwrite object to array", opts: &ApiOptions{ IO: ios, RawFields: []string{ "object[field][field2]=D", "object[field][]=this should be an error", }, }, expected: `expected array type under "field", got map[string]interface {}`, }, { name: "cannot overwrite array to string", opts: &ApiOptions{ IO: ios, RawFields: []string{ "object[field][]=E", "object[field]=this should be an error", }, }, expected: `unexpected override existing field under "field"`, }, { name: "cannot overwrite array to object", opts: &ApiOptions{ IO: ios, RawFields: []string{ "object[field][]=F", "object[field][field2]=this should be an error", }, }, expected: `expected map type under "field", got []interface {}`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := parseFields(tt.opts) require.EqualError(t, err, tt.expected) }) } } func Test_magicFieldValue(t *testing.T) { f, err := os.CreateTemp(t.TempDir(), "gh-test") if err != nil { t.Fatal(err) } defer f.Close() fmt.Fprint(f, "file contents") ios, _, _, _ := iostreams.Test() type args struct { v string opts *ApiOptions } tests := []struct { name string args args want interface{} wantErr bool }{ { name: "string", args: args{v: "hello"}, want: "hello", wantErr: false, }, { name: "bool true", args: args{v: "true"}, want: true, wantErr: false, }, { name: "bool false", args: args{v: "false"}, want: false, wantErr: false, }, { name: "null", args: args{v: "null"}, want: nil, wantErr: false, }, { name: "placeholder colon", args: args{ v: ":owner", opts: &ApiOptions{ IO: ios, BaseRepo: func() (ghrepo.Interface, error) { return ghrepo.New("hubot", "robot-uprising"), nil }, }, }, want: "hubot", wantErr: false, }, { name: "placeholder braces", args: args{ v: "{owner}", opts: &ApiOptions{ IO: ios, BaseRepo: func() (ghrepo.Interface, error) { return ghrepo.New("hubot", "robot-uprising"), nil }, }, }, want: "hubot", wantErr: false, }, { name: "file", args: args{ v: "@" + f.Name(), opts: &ApiOptions{IO: ios}, }, want: "file contents", wantErr: false, }, { name: "file error", args: args{ v: "@", opts: &ApiOptions{IO: ios}, }, want: nil, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := magicFieldValue(tt.args.v, tt.args.opts) if (err != nil) != tt.wantErr { t.Errorf("magicFieldValue() error = %v, wantErr %v", err, tt.wantErr) return } if tt.wantErr { return } assert.Equal(t, tt.want, got) }) } }