github.com/cli/cli@v1.14.1-0.20210902173923-1af6a669e342/pkg/cmdutil/json_flags_test.go (about) 1 package cmdutil 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "testing" 8 9 "github.com/cli/cli/pkg/iostreams" 10 "github.com/spf13/cobra" 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 ) 14 15 func TestAddJSONFlags(t *testing.T) { 16 tests := []struct { 17 name string 18 fields []string 19 args []string 20 wantsExport *exportFormat 21 wantsError string 22 }{ 23 { 24 name: "no JSON flag", 25 fields: []string{}, 26 args: []string{}, 27 wantsExport: nil, 28 }, 29 { 30 name: "empty JSON flag", 31 fields: []string{"one", "two"}, 32 args: []string{"--json"}, 33 wantsExport: nil, 34 wantsError: "Specify one or more comma-separated fields for `--json`:\n one\n two", 35 }, 36 { 37 name: "invalid JSON field", 38 fields: []string{"id", "number"}, 39 args: []string{"--json", "idontexist"}, 40 wantsExport: nil, 41 wantsError: "Unknown JSON field: \"idontexist\"\nAvailable fields:\n id\n number", 42 }, 43 { 44 name: "cannot combine --json with --web", 45 fields: []string{"id", "number", "title"}, 46 args: []string{"--json", "id", "--web"}, 47 wantsExport: nil, 48 wantsError: "cannot use `--web` with `--json`", 49 }, 50 { 51 name: "cannot use --jq without --json", 52 fields: []string{}, 53 args: []string{"--jq", ".number"}, 54 wantsExport: nil, 55 wantsError: "cannot use `--jq` without specifying `--json`", 56 }, 57 { 58 name: "cannot use --template without --json", 59 fields: []string{}, 60 args: []string{"--template", "{{.number}}"}, 61 wantsExport: nil, 62 wantsError: "cannot use `--template` without specifying `--json`", 63 }, 64 { 65 name: "with JSON fields", 66 fields: []string{"id", "number", "title"}, 67 args: []string{"--json", "number,title"}, 68 wantsExport: &exportFormat{ 69 fields: []string{"number", "title"}, 70 filter: "", 71 template: "", 72 }, 73 }, 74 { 75 name: "with jq filter", 76 fields: []string{"id", "number", "title"}, 77 args: []string{"--json", "number", "-q.number"}, 78 wantsExport: &exportFormat{ 79 fields: []string{"number"}, 80 filter: ".number", 81 template: "", 82 }, 83 }, 84 { 85 name: "with Go template", 86 fields: []string{"id", "number", "title"}, 87 args: []string{"--json", "number", "-t", "{{.number}}"}, 88 wantsExport: &exportFormat{ 89 fields: []string{"number"}, 90 filter: "", 91 template: "{{.number}}", 92 }, 93 }, 94 } 95 for _, tt := range tests { 96 t.Run(tt.name, func(t *testing.T) { 97 cmd := &cobra.Command{Run: func(*cobra.Command, []string) {}} 98 cmd.Flags().Bool("web", false, "") 99 var exporter Exporter 100 AddJSONFlags(cmd, &exporter, tt.fields) 101 cmd.SetArgs(tt.args) 102 cmd.SetOut(ioutil.Discard) 103 cmd.SetErr(ioutil.Discard) 104 _, err := cmd.ExecuteC() 105 if tt.wantsError == "" { 106 require.NoError(t, err) 107 } else { 108 assert.EqualError(t, err, tt.wantsError) 109 return 110 } 111 if tt.wantsExport == nil { 112 assert.Nil(t, exporter) 113 } else { 114 assert.Equal(t, tt.wantsExport, exporter) 115 } 116 }) 117 } 118 } 119 120 func Test_exportFormat_Write(t *testing.T) { 121 type args struct { 122 data interface{} 123 } 124 tests := []struct { 125 name string 126 exporter exportFormat 127 args args 128 wantW string 129 wantErr bool 130 }{ 131 { 132 name: "regular JSON output", 133 exporter: exportFormat{}, 134 args: args{ 135 data: map[string]string{"name": "hubot"}, 136 }, 137 wantW: "{\"name\":\"hubot\"}\n", 138 wantErr: false, 139 }, 140 { 141 name: "call ExportData", 142 exporter: exportFormat{fields: []string{"field1", "field2"}}, 143 args: args{ 144 data: &exportableItem{"item1"}, 145 }, 146 wantW: "{\"field1\":\"item1:field1\",\"field2\":\"item1:field2\"}\n", 147 wantErr: false, 148 }, 149 { 150 name: "recursively call ExportData", 151 exporter: exportFormat{fields: []string{"f1", "f2"}}, 152 args: args{ 153 data: map[string]interface{}{ 154 "s1": []exportableItem{{"i1"}, {"i2"}}, 155 "s2": []exportableItem{{"i3"}}, 156 }, 157 }, 158 wantW: "{\"s1\":[{\"f1\":\"i1:f1\",\"f2\":\"i1:f2\"},{\"f1\":\"i2:f1\",\"f2\":\"i2:f2\"}],\"s2\":[{\"f1\":\"i3:f1\",\"f2\":\"i3:f2\"}]}\n", 159 wantErr: false, 160 }, 161 { 162 name: "with jq filter", 163 exporter: exportFormat{filter: ".name"}, 164 args: args{ 165 data: map[string]string{"name": "hubot"}, 166 }, 167 wantW: "hubot\n", 168 wantErr: false, 169 }, 170 { 171 name: "with Go template", 172 exporter: exportFormat{template: "{{.name}}"}, 173 args: args{ 174 data: map[string]string{"name": "hubot"}, 175 }, 176 wantW: "hubot", 177 wantErr: false, 178 }, 179 } 180 for _, tt := range tests { 181 t.Run(tt.name, func(t *testing.T) { 182 w := &bytes.Buffer{} 183 io := &iostreams.IOStreams{ 184 Out: w, 185 } 186 if err := tt.exporter.Write(io, tt.args.data); (err != nil) != tt.wantErr { 187 t.Errorf("exportFormat.Write() error = %v, wantErr %v", err, tt.wantErr) 188 return 189 } 190 if gotW := w.String(); gotW != tt.wantW { 191 t.Errorf("exportFormat.Write() = %q, want %q", gotW, tt.wantW) 192 } 193 }) 194 } 195 } 196 197 type exportableItem struct { 198 Name string 199 } 200 201 func (e *exportableItem) ExportData(fields []string) *map[string]interface{} { 202 m := map[string]interface{}{} 203 for _, f := range fields { 204 m[f] = fmt.Sprintf("%s:%s", e.Name, f) 205 } 206 return &m 207 }