github.com/arunkumar7540/cli@v6.45.0+incompatible/integration/v6/isolated/curl_command_test.go (about) 1 package isolated 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "regexp" 9 "strings" 10 11 "code.cloudfoundry.org/cli/integration/helpers" 12 . "github.com/onsi/ginkgo" 13 . "github.com/onsi/gomega" 14 . "github.com/onsi/gomega/gbytes" 15 . "github.com/onsi/gomega/gexec" 16 ) 17 18 var _ = Describe("curl command", func() { 19 var ExpectHelpText = func(session *Session) { 20 Eventually(session).Should(Say(`NAME:\n`)) 21 Eventually(session).Should(Say(`curl - Executes a request to the targeted API endpoint\n`)) 22 Eventually(session).Should(Say(`\n`)) 23 24 Eventually(session).Should(Say(`USAGE:\n`)) 25 Eventually(session).Should(Say(`\s+cf curl PATH \[-iv\] \[-X METHOD\] \[-H HEADER\]\.\.\. \[-d DATA\] \[--output FILE\]`)) 26 Eventually(session).Should(Say(`\s+By default 'cf curl' will perform a GET to the specified PATH. If data`)) 27 Eventually(session).Should(Say(`\s+is provided via -d, a POST will be performed instead, and the Content-Type\n`)) 28 Eventually(session).Should(Say(`\s+will be set to application/json. You may override headers with -H and the\n`)) 29 Eventually(session).Should(Say(`\s+request method with -X.\n`)) 30 Eventually(session).Should(Say(`\s+For API documentation, please visit http://apidocs.cloudfoundry.org.\n`)) 31 Eventually(session).Should(Say(`\n`)) 32 33 Eventually(session).Should(Say(`EXAMPLES:\n`)) 34 Eventually(session).Should(Say(`\s+cf curl \"/v2/apps\" -X GET -H \"Content-Type: application/x-www-form-urlencoded\" -d 'q=name:myapp'`)) 35 Eventually(session).Should(Say(`\s+cf curl \"/v2/apps\" -d @/path/to/file`)) 36 Eventually(session).Should(Say(`\n`)) 37 38 Eventually(session).Should(Say(`OPTIONS:\n`)) 39 Eventually(session).Should(Say(`\s+-H\s+Custom headers to include in the request, flag can be specified multiple times`)) 40 Eventually(session).Should(Say(`\s+-X\s+HTTP method \(GET,POST,PUT,DELETE,etc\)`)) 41 Eventually(session).Should(Say(`\s+-d\s+HTTP data to include in the request body, or '@' followed by a file name to read the data from`)) 42 Eventually(session).Should(Say(`\s+--fail,\s+-f\s+Server errors return exit code 22`)) 43 Eventually(session).Should(Say(`\s+-i\s+Include response headers in the output`)) 44 Eventually(session).Should(Say(`\s+--output\s+Write curl body to FILE instead of stdout`)) 45 } 46 47 var ExpectRequestHeaders = func(session *Session) { 48 Eventually(session).Should(Say(`REQUEST: .+`)) 49 Eventually(session).Should(Say(`(GET|POST|PUT|DELETE) /v2/apps HTTP/1.1`)) 50 Eventually(session).Should(Say(`Host: .+`)) 51 Eventually(session).Should(Say(`Accept: .+`)) 52 Eventually(session).Should(Say(`Authorization:\s+\[PRIVATE DATA HIDDEN\]`)) 53 Eventually(session).Should(Say(`Content-Type: .+`)) 54 Eventually(session).Should(Say(`User-Agent: .+`)) 55 } 56 57 var ExpectResponseHeaders = func(session *Session) { 58 Eventually(session).Should(Say("HTTP/1.1 200 OK")) 59 Eventually(session).Should(Say(`Connection: .+`)) 60 Eventually(session).Should(Say(`Content-Length: .+`)) 61 Eventually(session).Should(Say(`Content-Type: .+`)) 62 Eventually(session).Should(Say(`Date: .+`)) 63 Eventually(session).Should(Say(`Server: .+`)) 64 Eventually(session).Should(Say(`X-Content-Type-Options: .+`)) 65 Eventually(session).Should(Say(`X-Vcap-Request-Id: .+`)) 66 } 67 68 Describe("Help Text", func() { 69 When("--help flag is set", func() { 70 It("Displays command usage to the output", func() { 71 session := helpers.CF("curl", "--help") 72 ExpectHelpText(session) 73 Eventually(session).Should(Exit(0)) 74 }) 75 }) 76 }) 77 78 Describe("Incorrect Usage", func() { 79 When("no arguments are provided", func() { 80 It("fails and displays the help text", func() { 81 session := helpers.CF("curl") 82 Eventually(session.Err).Should(Say("Incorrect Usage: the required argument `PATH` was not provided")) 83 ExpectHelpText(session) 84 Eventually(session).Should(Exit(1)) 85 }) 86 }) 87 88 When("unknown flag is specified", func() { 89 It("fails and displays the help text", func() { 90 session := helpers.CF("curl", "--test") 91 Eventually(session.Err).Should(Say("Incorrect Usage: unknown flag `test'")) 92 ExpectHelpText(session) 93 Eventually(session).Should(Exit(1)) 94 }) 95 }) 96 97 When("more than one path is specified", func() { 98 It("fails and displays the help text", func() { 99 session := helpers.CF("curl", "/v2/apps", "/v2/apps") 100 Eventually(session).Should(Say("FAILED\n")) 101 Eventually(session).Should(Say("Incorrect Usage. An argument is missing or not correctly enclosed.")) 102 ExpectHelpText(session) 103 Eventually(session).Should(Exit(1)) 104 }) 105 }) 106 }) 107 108 When("the user is not logged in", func() { 109 It("makes the request and receives an unauthenticated error", func() { 110 session := helpers.CF("curl", "/v2/apps") 111 expectedJSON := `{ 112 "description": "Authentication error", 113 "error_code": "CF-NotAuthenticated", 114 "code": 10002 115 }` 116 Eventually(session).Should(Exit(0)) 117 Expect(session.Out.Contents()).To(MatchJSON(expectedJSON)) 118 }) 119 }) 120 121 Describe("User Agent", func() { 122 It("sets the User-Agent Header to contain the CLI version", func() { 123 getVersionNumber := func() string { 124 versionSession := helpers.CF("version") 125 Eventually(versionSession).Should(Exit(0)) 126 versionPattern := regexp.MustCompile("cf version (.+)") 127 version := versionPattern.FindStringSubmatch(string(versionSession.Out.Contents())) 128 return regexp.QuoteMeta(version[1]) 129 } 130 session := helpers.CF("curl", "/v2/info", "-v") 131 Eventually(session).Should(Exit(0)) 132 133 Expect(session).To(Say(`User-Agent: go-cli %s`, getVersionNumber())) 134 }) 135 }) 136 137 When("the user is logged in", func() { 138 var orgName string 139 140 BeforeEach(func() { 141 orgName = helpers.NewOrgName() 142 spaceName := helpers.NewSpaceName() 143 144 helpers.SetupCF(orgName, spaceName) 145 helpers.SwitchToOrgRole(orgName, "OrgManager") 146 helpers.TargetOrg(orgName) 147 }) 148 149 When("the path is valid", func() { 150 var expectedJSON string 151 152 BeforeEach(func() { 153 expectedJSON = `{ 154 "total_results": 0, 155 "total_pages": 1, 156 "prev_url": null, 157 "next_url": null, 158 "resources": [] 159 }` 160 }) 161 162 When("the path has multiple initial slashes", func() { 163 It("changes the path to use only one slash", func() { 164 session := helpers.CF("curl", "////v2/apps", "-v") 165 Eventually(session).Should(Exit(0)) 166 167 Eventually(session).Should(Say(`GET /v2/apps HTTP/1.1`)) 168 }) 169 }) 170 171 When("the path has no initial slashes", func() { 172 It("prepends a slash to the path", func() { 173 session := helpers.CF("curl", "v2/apps", "-v") 174 Eventually(session).Should(Exit(0)) 175 176 Eventually(session).Should(Say(`GET /v2/apps HTTP/1.1`)) 177 }) 178 }) 179 180 When("no flag is set", func() { 181 It("makes the request and displays the json response", func() { 182 session := helpers.CF("curl", "/v2/apps") 183 Eventually(session).Should(Exit(0)) 184 Expect(session.Out.Contents()).To(MatchJSON(expectedJSON)) 185 }) 186 }) 187 188 When("-i flag is set", func() { 189 It("displays the response headers", func() { 190 session := helpers.CF("curl", "/v2/apps", "-i") 191 Eventually(session).Should(Exit(0)) 192 193 ExpectResponseHeaders(session) 194 contents := string(session.Out.Contents()) 195 jsonStartsAt := strings.Index(contents, "{") 196 197 actualJSON := contents[jsonStartsAt:] 198 Expect(actualJSON).To(MatchJSON(expectedJSON)) 199 }) 200 }) 201 202 When("-v flag is set", func() { 203 It("displays the request headers and response headers", func() { 204 session := helpers.CF("curl", "/v2/apps", "-v") 205 Eventually(session).Should(Exit(0)) 206 207 ExpectRequestHeaders(session) 208 ExpectResponseHeaders(session) 209 210 contents := string(session.Out.Contents()) 211 jsonStartsAt := strings.Index(contents, "{") 212 213 actualJSON := contents[jsonStartsAt:] 214 Expect(actualJSON).To(MatchJSON(expectedJSON)) 215 }) 216 }) 217 218 When("-H is passed with a custom header", func() { 219 When("the custom header is valid", func() { 220 It("add the custom header to the request", func() { 221 session := helpers.CF("curl", "/v2/apps", "-H", "X-Foo: bar", "-v") 222 Eventually(session).Should(Exit(0)) 223 224 Expect(session).To(Say("REQUEST:")) 225 Expect(session).To(Say("X-Foo: bar")) 226 Expect(session).To(Say("RESPONSE:")) 227 }) 228 229 When("multiple headers are provided", func() { 230 It("adds all the custom headers to the request", func() { 231 session := helpers.CF("curl", "/v2/apps", "-H", "X-Bar: bar", "-H", "X-Foo: foo", "-v") 232 Eventually(session).Should(Exit(0)) 233 234 Expect(session).To(Say("REQUEST:")) 235 Expect(session).To(Say("X-Bar: bar")) 236 Expect(session).To(Say("X-Foo: foo")) 237 Expect(session).To(Say("RESPONSE:")) 238 }) 239 240 When("the same header field is passed", func() { 241 It("adds the same header field twice", func() { 242 session := helpers.CF("curl", "/v2/apps", "-H", "X-Foo: bar", "-H", "X-Foo: foo", "-v") 243 Eventually(session).Should(Exit(0)) 244 245 Expect(session).To(Say("REQUEST:")) 246 Expect(session).To(Say("X-Foo: bar")) 247 Expect(session).To(Say("X-Foo: foo")) 248 Expect(session).To(Say("RESPONSE:")) 249 }) 250 }) 251 }) 252 253 When("-H is provided with a default header", func() { 254 It("overrides the value of User-Agent header", func() { 255 session := helpers.CF("curl", "/v2/apps", "-H", "User-Agent: smith", "-v") 256 Eventually(session).Should(Exit(0)) 257 258 Expect(session).To(Say("REQUEST:")) 259 Expect(session).To(Say("User-Agent: smith")) 260 Expect(session).To(Say("RESPONSE:")) 261 }) 262 263 It("does not override the Host header", func() { 264 getHost := func() string { 265 apiSession := helpers.CF("api") 266 Eventually(apiSession).Should(Exit(0)) 267 output := string(apiSession.Out.Contents()) 268 lines := strings.Split(output, "\n") 269 Expect(len(lines)).To(BeNumerically(">=", 1)) 270 parts := strings.Split(lines[0], "//") 271 Expect(len(parts)).To(BeNumerically(">=", 2)) 272 return parts[1] 273 } 274 session := helpers.CF("curl", "/v2/apps", "-H", "Host: example.com", "-v") 275 Eventually(session).Should(Exit(0)) 276 Expect(session).To(Say("Host: " + getHost())) 277 }) 278 279 It("overrides the value of Accept header", func() { 280 session := helpers.CF("curl", "/v2/apps", "-H", "Accept: application/xml", "-v") 281 Eventually(session).Should(Exit(0)) 282 283 Expect(session).To(Say("REQUEST:")) 284 Expect(session).To(Say("Accept: application/xml")) 285 Expect(session).To(Say("RESPONSE:")) 286 }) 287 288 It("overrides the value of Content-Type header", func() { 289 session := helpers.CF("curl", "/v2/apps", "-H", "Content-Type: application/xml", "-v") 290 Eventually(session).Should(Exit(0)) 291 292 Expect(session).To(Say("REQUEST:")) 293 Expect(session).To(Say("Content-Type: application/xml")) 294 Expect(session).To(Say("RESPONSE:")) 295 }) 296 }) 297 }) 298 299 When("the custom header is not valid", func() { 300 It("tells the user that the header is not valid", func() { 301 session := helpers.CF("curl", "/v2/apps", "-H", "not-a-valid-header", "-v") 302 Eventually(session).Should(Exit(1)) 303 304 Expect(session).Should(Say("FAILED")) 305 Expect(session).Should(Say("Error creating request:")) 306 Expect(session).Should(Say("Error parsing headers: malformed MIME header line: not-a-valid-header")) 307 }) 308 }) 309 }) 310 311 When("-d is passed with a request body", func() { 312 When("the request body is passed as a string", func() { 313 It("sets the method to POST and sends the body", func() { 314 orgGUID := helpers.GetOrgGUID(orgName) 315 spaceName := helpers.NewSpaceName() 316 jsonBody := fmt.Sprintf(`{"name":"%s", "organization_guid":"%s"}`, spaceName, orgGUID) 317 session := helpers.CF("curl", "/v2/spaces", "-d", jsonBody) 318 Eventually(session).Should(Exit(0)) 319 Eventually(helpers.CF("space", spaceName)).Should(Exit(0)) 320 }) 321 }) 322 323 When("the request body is passed as a file", func() { 324 var spaceName, filePath, dir string 325 326 BeforeEach(func() { 327 var err error 328 dir, err = ioutil.TempDir("", "curl-command") 329 Expect(err).ToNot(HaveOccurred()) 330 331 filePath = filepath.Join(dir, "request_body.json") 332 orgGUID := helpers.GetOrgGUID(orgName) 333 spaceName = helpers.NewSpaceName() 334 335 jsonBody := fmt.Sprintf(`{"name":"%s", "organization_guid":"%s"}`, spaceName, orgGUID) 336 err = ioutil.WriteFile(filePath, []byte(jsonBody), 0666) 337 Expect(err).ToNot(HaveOccurred()) 338 }) 339 340 AfterEach(func() { 341 os.RemoveAll(dir) 342 }) 343 344 It("sets the method to POST and sends the body", func() { 345 session := helpers.CF("curl", "/v2/spaces", "-d", "@"+filePath) 346 Eventually(session).Should(Exit(0)) 347 Eventually(helpers.CF("space", spaceName)).Should(Exit(0)) 348 }) 349 350 When("the file does not exist", func() { 351 It("fails and displays an error message", func() { 352 _, err := os.Stat("this-file-does-not-exist") 353 Expect(os.IsExist(err)).To(BeFalse()) 354 355 session := helpers.CF("curl", "/v2/spaces", "-d", "@this-file-does-not-exist") 356 Eventually(session).Should(Exit(1)) 357 Expect(session).To(Say("FAILED")) 358 }) 359 }) 360 361 When("the file is a symlink", func() { 362 It("follows the symlink", func() { 363 linkPath := filepath.Join(dir, "link-name.json") 364 Expect(os.Symlink(filePath, linkPath)).To(Succeed()) 365 session := helpers.CF("curl", "-d", "@"+linkPath, "/v2/spaces") 366 Eventually(session).Should(Exit(0)) 367 Eventually(helpers.CF("space", spaceName)).Should(Exit(0)) 368 }) 369 }) 370 }) 371 }) 372 373 When("-X is passed with the HTTP method", func() { 374 var spaceGUID, spaceName string 375 376 BeforeEach(func() { 377 spaceName = helpers.NewSpaceName() 378 helpers.CreateSpace(spaceName) 379 spaceGUID = helpers.GetSpaceGUID(spaceName) 380 }) 381 382 It("changes the HTTP method of the request", func() { 383 path := fmt.Sprintf("/v2/spaces/%s", spaceGUID) 384 session := helpers.CF("curl", path, "-X", "DELETE", "-v") 385 Eventually(session).Should(Exit(0)) 386 387 Eventually(helpers.CF("space", spaceName)).Should(Exit(1)) 388 }) 389 }) 390 391 When("--output is passed with a file name", func() { 392 It("writes the response body to the file but the other output to stdout", func() { 393 outFile, err := ioutil.TempFile("", "output*.json") 394 Expect(err).ToNot(HaveOccurred()) 395 session := helpers.CF("curl", "/v2/apps", "-i", "--output", outFile.Name()) 396 Eventually(session).Should(Exit(0)) 397 ExpectResponseHeaders(session) 398 body, err := ioutil.ReadFile(outFile.Name()) 399 Expect(err).ToNot(HaveOccurred()) 400 Expect(string(body)).To(MatchJSON(expectedJSON)) 401 }) 402 403 When("--output is passed and CF_TRACE is set to a file", func() { 404 var tempDir, traceFile, outFile string 405 BeforeEach(func() { 406 var err error 407 tempDir, err = ioutil.TempDir("", "") 408 Expect(err).ToNot(HaveOccurred()) 409 traceFile = filepath.Join(tempDir, "trace.log") 410 outFile = filepath.Join(tempDir, "output") 411 }) 412 413 AfterEach(func() { 414 Expect(os.RemoveAll(tempDir)).To(Succeed()) 415 }) 416 417 It("writes the response body to the --output file and everything to the trace file", func() { 418 session := helpers.CFWithEnv(map[string]string{"CF_TRACE": traceFile}, "curl", "/v2/apps", "--output", outFile) 419 Eventually(session).Should(Exit(0)) 420 421 outBytes, err := ioutil.ReadFile(outFile) 422 Expect(err).ToNot(HaveOccurred()) 423 Expect(string(outBytes)).To(MatchJSON(expectedJSON)) 424 425 traceBytes, err := ioutil.ReadFile(traceFile) 426 Expect(traceBytes).To(ContainSubstring("REQUEST: ")) 427 Expect(traceBytes).To(ContainSubstring("HTTP/1.1 200 OK")) 428 }) 429 }) 430 }) 431 432 Describe("Flag combinations", func() { 433 When("-i and -v flags are set", func() { 434 It("prints both the request and response headers", func() { 435 session := helpers.CF("curl", "/v2/apps", "-v", "-i") 436 Eventually(session).Should(Exit(0)) 437 438 ExpectRequestHeaders(session) 439 ExpectResponseHeaders(session) 440 441 contents := string(session.Out.Contents()) 442 jsonStartsAt := strings.Index(contents, "{") 443 444 actualJSON := contents[jsonStartsAt:] 445 Expect(actualJSON).To(MatchJSON(expectedJSON)) 446 }) 447 }) 448 449 XWhen("-v and --output flags are passed", func() { 450 It("prints the headers to the terminal and the response to the file", func() { 451 // TODO This is a bug in the legacy CLI. Please write the test and fix the bug after refactor [#162432878] 452 }) 453 }) 454 455 When("-X, -H and -d flags are passed", func() { 456 var spaceName, filePath, dir, jsonBody string 457 458 BeforeEach(func() { 459 var err error 460 dir, err = ioutil.TempDir("", "curl-command") 461 Expect(err).ToNot(HaveOccurred()) 462 463 filePath = filepath.Join(dir, "request_body.json") 464 orgGUID := helpers.GetOrgGUID(orgName) 465 spaceName = helpers.NewSpaceName() 466 467 jsonBody = fmt.Sprintf(`{"name":"%s", "organization_guid":"%s"}`, spaceName, orgGUID) 468 err = ioutil.WriteFile(filePath, []byte(jsonBody), 0666) 469 Expect(err).ToNot(HaveOccurred()) 470 }) 471 472 AfterEach(func() { 473 os.RemoveAll(dir) 474 }) 475 476 It("sets the custom header and use the request body from -d", func() { 477 session := helpers.CF("curl", "/v2/spaces", "-X", "POST", "-H", "X-Foo: foo", "-H", "X-Bar: bar", "-d", "@"+filePath, "-v") 478 Eventually(session).Should(Exit(0)) 479 480 Expect(session).Should(Say("REQUEST:")) 481 Expect(session).Should(Say("POST")) 482 483 Expect(session).Should(Say("X-Bar: bar")) 484 Expect(session).Should(Say("X-Foo: foo")) 485 486 contents := string(session.Out.Contents()) 487 outputContents := contents[strings.Index(contents, "X-Foo: foo"):] 488 jsonStartsAt := strings.Index(outputContents, "{") 489 jsonEndsAt := strings.Index(outputContents[jsonStartsAt:], "}") 490 491 actualJSON := outputContents[jsonStartsAt : jsonStartsAt+jsonEndsAt+1] 492 Expect(actualJSON).To(MatchJSON(jsonBody)) 493 494 Expect(session).Should(Say("RESPONSE:")) 495 496 Eventually(helpers.CF("space", spaceName)).Should(Exit(0)) 497 }) 498 }) 499 }) 500 501 When("the auth token is invalid", func() { 502 var spaceGUID, spaceName string 503 504 BeforeEach(func() { 505 spaceName = helpers.NewSpaceName() 506 helpers.CreateSpace(spaceName) 507 spaceGUID = helpers.GetSpaceGUID(spaceName) 508 }) 509 510 It("generates a new auth token by using the refresh token", func() { 511 path := fmt.Sprintf("/v2/spaces/%s", spaceGUID) 512 authHeader := fmt.Sprintf("Authorization: %s", helpers.ExpiredAccessToken()) 513 session := helpers.CF("curl", path, "-H", authHeader, "-X", "DELETE", "-v") 514 Eventually(session).Should(Exit(0)) 515 516 Expect(session).To(Say("POST /oauth/token")) 517 518 Eventually(helpers.CF("space", spaceName)).Should(Exit(1)) 519 }) 520 }) 521 }) 522 523 When("the path is invalid", func() { 524 It("makes the request and displays the unknown request json", func() { 525 expectedJSON := `{ 526 "description": "Unknown request", 527 "error_code": "CF-NotFound", 528 "code": 10000 529 }` 530 session := helpers.CF("curl", "/some-random-path") 531 Eventually(session).Should(Exit(0)) 532 Expect(session.Out.Contents()).To(MatchJSON(expectedJSON)) 533 }) 534 535 When("the -f flag is passed", func() { 536 It("should exit 22", func() { 537 session := helpers.CF("curl", "/v2/garbage-endpoint", "-f") 538 Eventually(session).Should(Exit(22)) 539 }) 540 }) 541 }) 542 }) 543 })