github.com/secman-team/gh-api@v1.8.2/pkg/cmd/repo/create/create_test.go (about) 1 package create 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "io/ioutil" 7 "net/http" 8 "testing" 9 10 "github.com/MakeNowJust/heredoc" 11 "github.com/secman-team/gh-api/core/config" 12 "github.com/secman-team/gh-api/core/run" 13 "github.com/secman-team/gh-api/pkg/cmdutil" 14 "github.com/secman-team/gh-api/pkg/httpmock" 15 "github.com/secman-team/gh-api/pkg/iostreams" 16 "github.com/secman-team/gh-api/pkg/prompt" 17 "github.com/secman-team/gh-api/test" 18 "github.com/google/shlex" 19 "github.com/stretchr/testify/assert" 20 ) 21 22 func runCommand(httpClient *http.Client, cli string, isTTY bool) (*test.CmdOut, error) { 23 io, _, stdout, stderr := iostreams.Test() 24 io.SetStdoutTTY(isTTY) 25 io.SetStdinTTY(isTTY) 26 fac := &cmdutil.Factory{ 27 IOStreams: io, 28 HttpClient: func() (*http.Client, error) { 29 return httpClient, nil 30 }, 31 Config: func() (config.Config, error) { 32 return config.NewBlankConfig(), nil 33 }, 34 } 35 36 cmd := NewCmdCreate(fac, nil) 37 38 // TODO STUPID HACK 39 // cobra aggressively adds help to all commands. since we're not running through the root command 40 // (which manages help when running for real) and since create has a '-h' flag (for homepage), 41 // cobra blows up when it tried to add a help flag and -h is already in use. This hack adds a 42 // dummy help flag with a random shorthand to get around this. 43 cmd.Flags().BoolP("help", "x", false, "") 44 45 argv, err := shlex.Split(cli) 46 cmd.SetArgs(argv) 47 48 cmd.SetIn(&bytes.Buffer{}) 49 cmd.SetOut(&bytes.Buffer{}) 50 cmd.SetErr(&bytes.Buffer{}) 51 52 if err != nil { 53 panic(err) 54 } 55 56 _, err = cmd.ExecuteC() 57 58 if err != nil { 59 return nil, err 60 } 61 62 return &test.CmdOut{ 63 OutBuf: stdout, 64 ErrBuf: stderr}, nil 65 } 66 67 func TestRepoCreate(t *testing.T) { 68 reg := &httpmock.Registry{} 69 reg.Register( 70 httpmock.GraphQL(`mutation RepositoryCreate\b`), 71 httpmock.StringResponse(` 72 { "data": { "createRepository": { 73 "repository": { 74 "id": "REPOID", 75 "url": "https://github.com/OWNER/REPO", 76 "name": "REPO", 77 "owner": { 78 "login": "OWNER" 79 } 80 } 81 } } }`)) 82 83 httpClient := &http.Client{Transport: reg} 84 85 cs, cmdTeardown := run.Stub() 86 defer cmdTeardown(t) 87 88 cs.Register(`git remote add -f origin https://github\.com/OWNER/REPO\.git`, 0, "") 89 cs.Register(`git rev-parse --show-toplevel`, 0, "") 90 91 as, surveyTearDown := prompt.InitAskStubber() 92 defer surveyTearDown() 93 94 as.Stub([]*prompt.QuestionStub{ 95 { 96 Name: "repoVisibility", 97 Value: "PRIVATE", 98 }, 99 }) 100 as.Stub([]*prompt.QuestionStub{ 101 { 102 Name: "confirmSubmit", 103 Value: true, 104 }, 105 }) 106 107 output, err := runCommand(httpClient, "REPO", true) 108 if err != nil { 109 t.Errorf("error running command `repo create`: %v", err) 110 } 111 112 assert.Equal(t, "", output.String()) 113 assert.Equal(t, "✓ Created repository OWNER/REPO on GitHub\n✓ Added remote https://github.com/OWNER/REPO.git\n", output.Stderr()) 114 115 var reqBody struct { 116 Query string 117 Variables struct { 118 Input map[string]interface{} 119 } 120 } 121 122 if len(reg.Requests) != 1 { 123 t.Fatalf("expected 1 HTTP request, got %d", len(reg.Requests)) 124 } 125 126 bodyBytes, _ := ioutil.ReadAll(reg.Requests[0].Body) 127 _ = json.Unmarshal(bodyBytes, &reqBody) 128 if repoName := reqBody.Variables.Input["name"].(string); repoName != "REPO" { 129 t.Errorf("expected %q, got %q", "REPO", repoName) 130 } 131 if repoVisibility := reqBody.Variables.Input["visibility"].(string); repoVisibility != "PRIVATE" { 132 t.Errorf("expected %q, got %q", "PRIVATE", repoVisibility) 133 } 134 if _, ownerSet := reqBody.Variables.Input["ownerId"]; ownerSet { 135 t.Error("expected ownerId not to be set") 136 } 137 } 138 139 func TestRepoCreate_outsideGitWorkDir(t *testing.T) { 140 reg := &httpmock.Registry{} 141 reg.Register( 142 httpmock.GraphQL(`mutation RepositoryCreate\b`), 143 httpmock.StringResponse(` 144 { "data": { "createRepository": { 145 "repository": { 146 "id": "REPOID", 147 "url": "https://github.com/OWNER/REPO", 148 "name": "REPO", 149 "owner": { 150 "login": "OWNER" 151 } 152 } 153 } } }`)) 154 155 httpClient := &http.Client{Transport: reg} 156 157 cs, cmdTeardown := run.Stub() 158 defer cmdTeardown(t) 159 160 cs.Register(`git rev-parse --show-toplevel`, 1, "") 161 cs.Register(`git init REPO`, 0, "") 162 cs.Register(`git -C REPO remote add origin https://github\.com/OWNER/REPO\.git`, 0, "") 163 164 output, err := runCommand(httpClient, "REPO --private --confirm", false) 165 if err != nil { 166 t.Errorf("error running command `repo create`: %v", err) 167 } 168 169 assert.Equal(t, "https://github.com/OWNER/REPO\n", output.String()) 170 assert.Equal(t, "", output.Stderr()) 171 172 var reqBody struct { 173 Query string 174 Variables struct { 175 Input map[string]interface{} 176 } 177 } 178 179 if len(reg.Requests) != 1 { 180 t.Fatalf("expected 1 HTTP request, got %d", len(reg.Requests)) 181 } 182 183 bodyBytes, _ := ioutil.ReadAll(reg.Requests[0].Body) 184 _ = json.Unmarshal(bodyBytes, &reqBody) 185 if repoName := reqBody.Variables.Input["name"].(string); repoName != "REPO" { 186 t.Errorf("expected %q, got %q", "REPO", repoName) 187 } 188 if repoVisibility := reqBody.Variables.Input["visibility"].(string); repoVisibility != "PRIVATE" { 189 t.Errorf("expected %q, got %q", "PRIVATE", repoVisibility) 190 } 191 if _, ownerSet := reqBody.Variables.Input["ownerId"]; ownerSet { 192 t.Error("expected ownerId not to be set") 193 } 194 } 195 196 func TestRepoCreate_org(t *testing.T) { 197 reg := &httpmock.Registry{} 198 reg.Register( 199 httpmock.REST("GET", "users/ORG"), 200 httpmock.StringResponse(` 201 { "node_id": "ORGID" 202 }`)) 203 reg.Register( 204 httpmock.GraphQL(`mutation RepositoryCreate\b`), 205 httpmock.StringResponse(` 206 { "data": { "createRepository": { 207 "repository": { 208 "id": "REPOID", 209 "url": "https://github.com/ORG/REPO", 210 "name": "REPO", 211 "owner": { 212 "login": "ORG" 213 } 214 } 215 } } }`)) 216 httpClient := &http.Client{Transport: reg} 217 218 cs, cmdTeardown := run.Stub() 219 defer cmdTeardown(t) 220 221 cs.Register(`git remote add -f origin https://github\.com/ORG/REPO\.git`, 0, "") 222 cs.Register(`git rev-parse --show-toplevel`, 0, "") 223 224 as, surveyTearDown := prompt.InitAskStubber() 225 defer surveyTearDown() 226 227 as.Stub([]*prompt.QuestionStub{ 228 { 229 Name: "repoVisibility", 230 Value: "PRIVATE", 231 }, 232 }) 233 as.Stub([]*prompt.QuestionStub{ 234 { 235 Name: "confirmSubmit", 236 Value: true, 237 }, 238 }) 239 240 output, err := runCommand(httpClient, "ORG/REPO", true) 241 if err != nil { 242 t.Errorf("error running command `repo create`: %v", err) 243 } 244 245 assert.Equal(t, "", output.String()) 246 assert.Equal(t, "✓ Created repository ORG/REPO on GitHub\n✓ Added remote https://github.com/ORG/REPO.git\n", output.Stderr()) 247 248 var reqBody struct { 249 Query string 250 Variables struct { 251 Input map[string]interface{} 252 } 253 } 254 255 if len(reg.Requests) != 2 { 256 t.Fatalf("expected 2 HTTP requests, got %d", len(reg.Requests)) 257 } 258 259 assert.Equal(t, "/users/ORG", reg.Requests[0].URL.Path) 260 261 bodyBytes, _ := ioutil.ReadAll(reg.Requests[1].Body) 262 _ = json.Unmarshal(bodyBytes, &reqBody) 263 if orgID := reqBody.Variables.Input["ownerId"].(string); orgID != "ORGID" { 264 t.Errorf("expected %q, got %q", "ORGID", orgID) 265 } 266 if _, teamSet := reqBody.Variables.Input["teamId"]; teamSet { 267 t.Error("expected teamId not to be set") 268 } 269 } 270 271 func TestRepoCreate_orgWithTeam(t *testing.T) { 272 reg := &httpmock.Registry{} 273 reg.Register( 274 httpmock.REST("GET", "orgs/ORG/teams/monkeys"), 275 httpmock.StringResponse(` 276 { "node_id": "TEAMID", 277 "organization": { "node_id": "ORGID" } 278 }`)) 279 reg.Register( 280 httpmock.GraphQL(`mutation RepositoryCreate\b`), 281 httpmock.StringResponse(` 282 { "data": { "createRepository": { 283 "repository": { 284 "id": "REPOID", 285 "url": "https://github.com/ORG/REPO", 286 "name": "REPO", 287 "owner": { 288 "login": "ORG" 289 } 290 } 291 } } }`)) 292 httpClient := &http.Client{Transport: reg} 293 294 cs, cmdTeardown := run.Stub() 295 defer cmdTeardown(t) 296 297 cs.Register(`git remote add -f origin https://github\.com/ORG/REPO\.git`, 0, "") 298 cs.Register(`git rev-parse --show-toplevel`, 0, "") 299 300 as, surveyTearDown := prompt.InitAskStubber() 301 defer surveyTearDown() 302 303 as.Stub([]*prompt.QuestionStub{ 304 { 305 Name: "repoVisibility", 306 Value: "PRIVATE", 307 }, 308 }) 309 as.Stub([]*prompt.QuestionStub{ 310 { 311 Name: "confirmSubmit", 312 Value: true, 313 }, 314 }) 315 316 output, err := runCommand(httpClient, "ORG/REPO --team monkeys", true) 317 if err != nil { 318 t.Errorf("error running command `repo create`: %v", err) 319 } 320 321 assert.Equal(t, "", output.String()) 322 assert.Equal(t, "✓ Created repository ORG/REPO on GitHub\n✓ Added remote https://github.com/ORG/REPO.git\n", output.Stderr()) 323 324 var reqBody struct { 325 Query string 326 Variables struct { 327 Input map[string]interface{} 328 } 329 } 330 331 if len(reg.Requests) != 2 { 332 t.Fatalf("expected 2 HTTP requests, got %d", len(reg.Requests)) 333 } 334 335 assert.Equal(t, "/orgs/ORG/teams/monkeys", reg.Requests[0].URL.Path) 336 337 bodyBytes, _ := ioutil.ReadAll(reg.Requests[1].Body) 338 _ = json.Unmarshal(bodyBytes, &reqBody) 339 if orgID := reqBody.Variables.Input["ownerId"].(string); orgID != "ORGID" { 340 t.Errorf("expected %q, got %q", "ORGID", orgID) 341 } 342 if teamID := reqBody.Variables.Input["teamId"].(string); teamID != "TEAMID" { 343 t.Errorf("expected %q, got %q", "TEAMID", teamID) 344 } 345 } 346 347 func TestRepoCreate_template(t *testing.T) { 348 reg := &httpmock.Registry{} 349 defer reg.Verify(t) 350 reg.Register( 351 httpmock.GraphQL(`mutation CloneTemplateRepository\b`), 352 httpmock.StringResponse(` 353 { "data": { "cloneTemplateRepository": { 354 "repository": { 355 "id": "REPOID", 356 "name": "REPO", 357 "owner": { 358 "login": "OWNER" 359 }, 360 "url": "https://github.com/OWNER/REPO" 361 } 362 } } }`)) 363 364 reg.StubRepoInfoResponse("OWNER", "REPO", "main") 365 366 reg.Register( 367 httpmock.GraphQL(`query UserCurrent\b`), 368 httpmock.StringResponse(`{"data":{"viewer":{"ID":"OWNERID"}}}`)) 369 370 httpClient := &http.Client{Transport: reg} 371 372 cs, cmdTeardown := run.Stub() 373 defer cmdTeardown(t) 374 375 cs.Register(`git rev-parse --show-toplevel`, 1, "") 376 cs.Register(`git init REPO`, 0, "") 377 cs.Register(`git -C REPO remote add`, 0, "") 378 cs.Register(`git -C REPO fetch origin \+refs/heads/main:refs/remotes/origin/main`, 0, "") 379 cs.Register(`git -C REPO checkout main`, 0, "") 380 381 _, surveyTearDown := prompt.InitAskStubber() 382 defer surveyTearDown() 383 384 output, err := runCommand(httpClient, "REPO -y --private --template='OWNER/REPO'", true) 385 if err != nil { 386 t.Errorf("error running command `repo create`: %v", err) 387 return 388 } 389 390 assert.Equal(t, "", output.String()) 391 assert.Equal(t, heredoc.Doc(` 392 ✓ Created repository OWNER/REPO on GitHub 393 ✓ Initialized repository in "REPO" 394 `), output.Stderr()) 395 396 var reqBody struct { 397 Query string 398 Variables struct { 399 Input map[string]interface{} 400 } 401 } 402 403 bodyBytes, _ := ioutil.ReadAll(reg.Requests[2].Body) 404 _ = json.Unmarshal(bodyBytes, &reqBody) 405 if repoName := reqBody.Variables.Input["name"].(string); repoName != "REPO" { 406 t.Errorf("expected %q, got %q", "REPO", repoName) 407 } 408 if repoVisibility := reqBody.Variables.Input["visibility"].(string); repoVisibility != "PRIVATE" { 409 t.Errorf("expected %q, got %q", "PRIVATE", repoVisibility) 410 } 411 if ownerId := reqBody.Variables.Input["ownerId"].(string); ownerId != "OWNERID" { 412 t.Errorf("expected %q, got %q", "OWNERID", ownerId) 413 } 414 } 415 416 func TestRepoCreate_withoutNameArg(t *testing.T) { 417 reg := &httpmock.Registry{} 418 reg.Register( 419 httpmock.REST("GET", "users/OWNER"), 420 httpmock.StringResponse(`{ "node_id": "OWNERID" }`)) 421 reg.Register( 422 httpmock.GraphQL(`mutation RepositoryCreate\b`), 423 httpmock.StringResponse(` 424 { "data": { "createRepository": { 425 "repository": { 426 "id": "REPOID", 427 "url": "https://github.com/OWNER/REPO", 428 "name": "REPO", 429 "owner": { 430 "login": "OWNER" 431 } 432 } 433 } } }`)) 434 httpClient := &http.Client{Transport: reg} 435 436 cs, cmdTeardown := run.Stub() 437 defer cmdTeardown(t) 438 439 cs.Register(`git remote add -f origin https://github\.com/OWNER/REPO\.git`, 0, "") 440 cs.Register(`git rev-parse --show-toplevel`, 0, "") 441 442 as, surveyTearDown := prompt.InitAskStubber() 443 defer surveyTearDown() 444 445 as.Stub([]*prompt.QuestionStub{ 446 { 447 Name: "repoName", 448 Value: "OWNER/REPO", 449 }, 450 { 451 Name: "repoDescription", 452 Value: "DESCRIPTION", 453 }, 454 { 455 Name: "repoVisibility", 456 Value: "PRIVATE", 457 }, 458 }) 459 as.Stub([]*prompt.QuestionStub{ 460 { 461 Name: "confirmSubmit", 462 Value: true, 463 }, 464 }) 465 466 output, err := runCommand(httpClient, "", true) 467 if err != nil { 468 t.Errorf("error running command `repo create`: %v", err) 469 } 470 471 assert.Equal(t, "", output.String()) 472 assert.Equal(t, "✓ Created repository OWNER/REPO on GitHub\n✓ Added remote https://github.com/OWNER/REPO.git\n", output.Stderr()) 473 474 var reqBody struct { 475 Query string 476 Variables struct { 477 Input map[string]interface{} 478 } 479 } 480 481 if len(reg.Requests) != 2 { 482 t.Fatalf("expected 2 HTTP request, got %d", len(reg.Requests)) 483 } 484 485 bodyBytes, _ := ioutil.ReadAll(reg.Requests[1].Body) 486 _ = json.Unmarshal(bodyBytes, &reqBody) 487 if repoName := reqBody.Variables.Input["name"].(string); repoName != "REPO" { 488 t.Errorf("expected %q, got %q", "REPO", repoName) 489 } 490 if repoVisibility := reqBody.Variables.Input["visibility"].(string); repoVisibility != "PRIVATE" { 491 t.Errorf("expected %q, got %q", "PRIVATE", repoVisibility) 492 } 493 if ownerId := reqBody.Variables.Input["ownerId"].(string); ownerId != "OWNERID" { 494 t.Errorf("expected %q, got %q", "OWNERID", ownerId) 495 } 496 }