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