github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/gist/create/create_test.go (about) 1 package create 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "io" 7 "net/http" 8 "os" 9 "path/filepath" 10 "strings" 11 "testing" 12 13 "github.com/MakeNowJust/heredoc" 14 "github.com/ungtb10d/cli/v2/internal/browser" 15 "github.com/ungtb10d/cli/v2/internal/config" 16 "github.com/ungtb10d/cli/v2/internal/run" 17 "github.com/ungtb10d/cli/v2/pkg/cmd/gist/shared" 18 "github.com/ungtb10d/cli/v2/pkg/cmdutil" 19 "github.com/ungtb10d/cli/v2/pkg/httpmock" 20 "github.com/ungtb10d/cli/v2/pkg/iostreams" 21 "github.com/google/shlex" 22 "github.com/stretchr/testify/assert" 23 ) 24 25 func Test_processFiles(t *testing.T) { 26 fakeStdin := strings.NewReader("hey cool how is it going") 27 files, err := processFiles(io.NopCloser(fakeStdin), "", []string{"-"}) 28 if err != nil { 29 t.Fatalf("unexpected error processing files: %s", err) 30 } 31 32 assert.Equal(t, 1, len(files)) 33 assert.Equal(t, "hey cool how is it going", files["gistfile0.txt"].Content) 34 } 35 36 func Test_guessGistName_stdin(t *testing.T) { 37 files := map[string]*shared.GistFile{ 38 "gistfile0.txt": {Content: "sample content"}, 39 } 40 41 gistName := guessGistName(files) 42 assert.Equal(t, "", gistName) 43 } 44 45 func Test_guessGistName_userFiles(t *testing.T) { 46 files := map[string]*shared.GistFile{ 47 "fig.txt": {Content: "I am a fig"}, 48 "apple.txt": {Content: "I am an apple"}, 49 "gistfile0.txt": {Content: "sample content"}, 50 } 51 52 gistName := guessGistName(files) 53 assert.Equal(t, "apple.txt", gistName) 54 } 55 56 func TestNewCmdCreate(t *testing.T) { 57 tests := []struct { 58 name string 59 cli string 60 factory func(*cmdutil.Factory) *cmdutil.Factory 61 wants CreateOptions 62 wantsErr bool 63 }{ 64 { 65 name: "no arguments", 66 cli: "", 67 wants: CreateOptions{ 68 Description: "", 69 Public: false, 70 Filenames: []string{""}, 71 }, 72 wantsErr: false, 73 }, 74 { 75 name: "no arguments with TTY stdin", 76 factory: func(f *cmdutil.Factory) *cmdutil.Factory { 77 f.IOStreams.SetStdinTTY(true) 78 return f 79 }, 80 cli: "", 81 wants: CreateOptions{ 82 Description: "", 83 Public: false, 84 Filenames: []string{""}, 85 }, 86 wantsErr: true, 87 }, 88 { 89 name: "stdin argument", 90 cli: "-", 91 wants: CreateOptions{ 92 Description: "", 93 Public: false, 94 Filenames: []string{"-"}, 95 }, 96 wantsErr: false, 97 }, 98 { 99 name: "with description", 100 cli: `-d "my new gist" -`, 101 wants: CreateOptions{ 102 Description: "my new gist", 103 Public: false, 104 Filenames: []string{"-"}, 105 }, 106 wantsErr: false, 107 }, 108 { 109 name: "public", 110 cli: `--public -`, 111 wants: CreateOptions{ 112 Description: "", 113 Public: true, 114 Filenames: []string{"-"}, 115 }, 116 wantsErr: false, 117 }, 118 { 119 name: "list of files", 120 cli: "file1.txt file2.txt", 121 wants: CreateOptions{ 122 Description: "", 123 Public: false, 124 Filenames: []string{"file1.txt", "file2.txt"}, 125 }, 126 wantsErr: false, 127 }, 128 } 129 for _, tt := range tests { 130 t.Run(tt.name, func(t *testing.T) { 131 ios, _, _, _ := iostreams.Test() 132 f := &cmdutil.Factory{ 133 IOStreams: ios, 134 } 135 136 if tt.factory != nil { 137 f = tt.factory(f) 138 } 139 140 argv, err := shlex.Split(tt.cli) 141 assert.NoError(t, err) 142 143 var gotOpts *CreateOptions 144 cmd := NewCmdCreate(f, func(opts *CreateOptions) error { 145 gotOpts = opts 146 return nil 147 }) 148 cmd.SetArgs(argv) 149 cmd.SetIn(&bytes.Buffer{}) 150 cmd.SetOut(&bytes.Buffer{}) 151 cmd.SetErr(&bytes.Buffer{}) 152 153 _, err = cmd.ExecuteC() 154 if tt.wantsErr { 155 assert.Error(t, err) 156 return 157 } 158 assert.NoError(t, err) 159 160 assert.Equal(t, tt.wants.Description, gotOpts.Description) 161 assert.Equal(t, tt.wants.Public, gotOpts.Public) 162 }) 163 } 164 } 165 166 func Test_createRun(t *testing.T) { 167 tempDir := t.TempDir() 168 fixtureFile := filepath.Join(tempDir, "fixture.txt") 169 assert.NoError(t, os.WriteFile(fixtureFile, []byte("{}"), 0644)) 170 emptyFile := filepath.Join(tempDir, "empty.txt") 171 assert.NoError(t, os.WriteFile(emptyFile, []byte(" \t\n"), 0644)) 172 173 tests := []struct { 174 name string 175 opts *CreateOptions 176 stdin string 177 wantOut string 178 wantStderr string 179 wantParams map[string]interface{} 180 wantErr bool 181 wantBrowse string 182 responseStatus int 183 }{ 184 { 185 name: "public", 186 opts: &CreateOptions{ 187 Public: true, 188 Filenames: []string{fixtureFile}, 189 }, 190 wantOut: "https://gist.github.com/aa5a315d61ae9438b18d\n", 191 wantStderr: "- Creating gist fixture.txt\n✓ Created public gist fixture.txt\n", 192 wantErr: false, 193 wantParams: map[string]interface{}{ 194 "description": "", 195 "updated_at": "0001-01-01T00:00:00Z", 196 "public": true, 197 "files": map[string]interface{}{ 198 "fixture.txt": map[string]interface{}{ 199 "content": "{}", 200 }, 201 }, 202 }, 203 responseStatus: http.StatusOK, 204 }, 205 { 206 name: "with description", 207 opts: &CreateOptions{ 208 Description: "an incredibly interesting gist", 209 Filenames: []string{fixtureFile}, 210 }, 211 wantOut: "https://gist.github.com/aa5a315d61ae9438b18d\n", 212 wantStderr: "- Creating gist fixture.txt\n✓ Created secret gist fixture.txt\n", 213 wantErr: false, 214 wantParams: map[string]interface{}{ 215 "description": "an incredibly interesting gist", 216 "updated_at": "0001-01-01T00:00:00Z", 217 "public": false, 218 "files": map[string]interface{}{ 219 "fixture.txt": map[string]interface{}{ 220 "content": "{}", 221 }, 222 }, 223 }, 224 responseStatus: http.StatusOK, 225 }, 226 { 227 name: "multiple files", 228 opts: &CreateOptions{ 229 Filenames: []string{fixtureFile, "-"}, 230 }, 231 stdin: "cool stdin content", 232 wantOut: "https://gist.github.com/aa5a315d61ae9438b18d\n", 233 wantStderr: "- Creating gist with multiple files\n✓ Created secret gist fixture.txt\n", 234 wantErr: false, 235 wantParams: map[string]interface{}{ 236 "description": "", 237 "updated_at": "0001-01-01T00:00:00Z", 238 "public": false, 239 "files": map[string]interface{}{ 240 "fixture.txt": map[string]interface{}{ 241 "content": "{}", 242 }, 243 "gistfile1.txt": map[string]interface{}{ 244 "content": "cool stdin content", 245 }, 246 }, 247 }, 248 responseStatus: http.StatusOK, 249 }, 250 { 251 name: "file with empty content", 252 opts: &CreateOptions{ 253 Filenames: []string{emptyFile}, 254 }, 255 wantOut: "", 256 wantStderr: heredoc.Doc(` 257 - Creating gist empty.txt 258 X Failed to create gist: a gist file cannot be blank 259 `), 260 wantErr: true, 261 wantParams: map[string]interface{}{ 262 "description": "", 263 "updated_at": "0001-01-01T00:00:00Z", 264 "public": false, 265 "files": map[string]interface{}{ 266 "empty.txt": map[string]interface{}{"content": " \t\n"}, 267 }, 268 }, 269 responseStatus: http.StatusUnprocessableEntity, 270 }, 271 { 272 name: "stdin arg", 273 opts: &CreateOptions{ 274 Filenames: []string{"-"}, 275 }, 276 stdin: "cool stdin content", 277 wantOut: "https://gist.github.com/aa5a315d61ae9438b18d\n", 278 wantStderr: "- Creating gist...\n✓ Created gist\n", 279 wantErr: false, 280 wantParams: map[string]interface{}{ 281 "description": "", 282 "updated_at": "0001-01-01T00:00:00Z", 283 "public": false, 284 "files": map[string]interface{}{ 285 "gistfile0.txt": map[string]interface{}{ 286 "content": "cool stdin content", 287 }, 288 }, 289 }, 290 responseStatus: http.StatusOK, 291 }, 292 { 293 name: "web arg", 294 opts: &CreateOptions{ 295 WebMode: true, 296 Filenames: []string{fixtureFile}, 297 }, 298 wantOut: "Opening gist.github.com/aa5a315d61ae9438b18d in your browser.\n", 299 wantStderr: "- Creating gist fixture.txt\n✓ Created secret gist fixture.txt\n", 300 wantErr: false, 301 wantBrowse: "https://gist.github.com/aa5a315d61ae9438b18d", 302 wantParams: map[string]interface{}{ 303 "description": "", 304 "updated_at": "0001-01-01T00:00:00Z", 305 "public": false, 306 "files": map[string]interface{}{ 307 "fixture.txt": map[string]interface{}{ 308 "content": "{}", 309 }, 310 }, 311 }, 312 responseStatus: http.StatusOK, 313 }, 314 } 315 for _, tt := range tests { 316 reg := &httpmock.Registry{} 317 if tt.responseStatus == http.StatusOK { 318 reg.Register( 319 httpmock.REST("POST", "gists"), 320 httpmock.StringResponse(`{ 321 "html_url": "https://gist.github.com/aa5a315d61ae9438b18d" 322 }`)) 323 } else { 324 reg.Register( 325 httpmock.REST("POST", "gists"), 326 httpmock.StatusStringResponse(tt.responseStatus, "{}")) 327 } 328 329 mockClient := func() (*http.Client, error) { 330 return &http.Client{Transport: reg}, nil 331 } 332 tt.opts.HttpClient = mockClient 333 334 tt.opts.Config = func() (config.Config, error) { 335 return config.NewBlankConfig(), nil 336 } 337 338 ios, stdin, stdout, stderr := iostreams.Test() 339 tt.opts.IO = ios 340 341 browser := &browser.Stub{} 342 tt.opts.Browser = browser 343 344 _, teardown := run.Stub() 345 defer teardown(t) 346 347 t.Run(tt.name, func(t *testing.T) { 348 stdin.WriteString(tt.stdin) 349 350 if err := createRun(tt.opts); (err != nil) != tt.wantErr { 351 t.Errorf("createRun() error = %v, wantErr %v", err, tt.wantErr) 352 } 353 bodyBytes, _ := io.ReadAll(reg.Requests[0].Body) 354 reqBody := make(map[string]interface{}) 355 err := json.Unmarshal(bodyBytes, &reqBody) 356 if err != nil { 357 t.Fatalf("error decoding JSON: %v", err) 358 } 359 assert.Equal(t, tt.wantOut, stdout.String()) 360 assert.Equal(t, tt.wantStderr, stderr.String()) 361 assert.Equal(t, tt.wantParams, reqBody) 362 reg.Verify(t) 363 browser.Verify(t, tt.wantBrowse) 364 }) 365 } 366 } 367 368 func Test_detectEmptyFiles(t *testing.T) { 369 tests := []struct { 370 content string 371 isEmptyFile bool 372 }{ 373 { 374 content: "{}", 375 isEmptyFile: false, 376 }, 377 { 378 content: "\n\t", 379 isEmptyFile: true, 380 }, 381 } 382 383 for _, tt := range tests { 384 files := map[string]*shared.GistFile{} 385 files["file"] = &shared.GistFile{ 386 Content: tt.content, 387 } 388 389 isEmptyFile := detectEmptyFiles(files) 390 assert.Equal(t, tt.isEmptyFile, isEmptyFile) 391 } 392 }