From 211081c0a8596588c8b50a2034ca474635588815 Mon Sep 17 00:00:00 2001 From: Anand Gaitonde <agaitonde@pivotal.io> Date: Wed, 27 Mar 2019 15:17:33 -0700 Subject: [PATCH] move integration tests into version specific suites V7 CLI doesn't support router groups; these tests are split so the V7 CLI no longer needs to check for anything related to the Router client. [#162997997] Authored-by: Anand Gaitonde <agaitonde@pivotal.io> --- .../isolated/create_route_command_test.go | 0 .../isolated/verbose_flag_test.go | 0 .../v7/isolated/create_route_command_test.go | 357 ++++++++++++++++++ integration/v7/isolated/verbose_flag_test.go | 312 +++++++++++++++ 4 files changed, 669 insertions(+) rename integration/{shared => v6}/isolated/create_route_command_test.go (100%) rename integration/{shared => v6}/isolated/verbose_flag_test.go (100%) create mode 100644 integration/v7/isolated/create_route_command_test.go create mode 100644 integration/v7/isolated/verbose_flag_test.go diff --git a/integration/shared/isolated/create_route_command_test.go b/integration/v6/isolated/create_route_command_test.go similarity index 100% rename from integration/shared/isolated/create_route_command_test.go rename to integration/v6/isolated/create_route_command_test.go diff --git a/integration/shared/isolated/verbose_flag_test.go b/integration/v6/isolated/verbose_flag_test.go similarity index 100% rename from integration/shared/isolated/verbose_flag_test.go rename to integration/v6/isolated/verbose_flag_test.go diff --git a/integration/v7/isolated/create_route_command_test.go b/integration/v7/isolated/create_route_command_test.go new file mode 100644 index 00000000000..d9ec00ab071 --- /dev/null +++ b/integration/v7/isolated/create_route_command_test.go @@ -0,0 +1,357 @@ +package isolated + +import ( + "fmt" + + "code.cloudfoundry.org/cli/integration/helpers" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gbytes" + . "github.com/onsi/gomega/gexec" +) + +var _ = Describe("create-route command", func() { + Context("Help", func() { + It("displays the help information", func() { + session := helpers.CF("create-route", "--help") + Eventually(session).Should(Say(`NAME:`)) + Eventually(session).Should(Say(`create-route - Create a url route in a space for later use\n`)) + Eventually(session).Should(Say(`\n`)) + + Eventually(session).Should(Say(`USAGE:`)) + Eventually(session).Should(Say(`Create an HTTP route:`)) + Eventually(session).Should(Say(`cf create-route SPACE DOMAIN \[--hostname HOSTNAME\] \[--path PATH\]\n`)) + Eventually(session).Should(Say(`\n`)) + + Eventually(session).Should(Say(`Create a TCP route:`)) + Eventually(session).Should(Say(`cf create-route SPACE DOMAIN \(--port PORT \| --random-port\)\n`)) + Eventually(session).Should(Say(`\n`)) + + Eventually(session).Should(Say(`EXAMPLES:`)) + Eventually(session).Should(Say(`cf create-route my-space example.com\s+# example.com`)) + Eventually(session).Should(Say(`cf create-route my-space example.com --hostname myapp\s+# myapp.example.com`)) + Eventually(session).Should(Say(`cf create-route my-space example.com --hostname myapp --path foo\s+# myapp.example.com/foo`)) + Eventually(session).Should(Say(`cf create-route my-space example.com --port 5000\s+# example.com:5000\n`)) + Eventually(session).Should(Say(`\n`)) + + Eventually(session).Should(Say(`OPTIONS:`)) + Eventually(session).Should(Say(`--hostname, -n\s+Hostname for the HTTP route \(required for shared domains\)`)) + Eventually(session).Should(Say(`--path\s+Path for the HTTP route`)) + Eventually(session).Should(Say(`--port\s+Port for the TCP route`)) + Eventually(session).Should(Say(`--random-port\s+Create a random port for the TCP route\n`)) + Eventually(session).Should(Say(`\n`)) + + Eventually(session).Should(Say(`SEE ALSO:`)) + Eventually(session).Should(Say(`check-route, domains, map-route`)) + + Eventually(session).Should(Exit(0)) + }) + }) + + Context("Flag Errors", func() { + When("--hostname and --port are provided", func() { + It("fails with a message about being unable to mix --port with the HTTP route options", func() { + session := helpers.CF("create-route", "some-space", "some-domain", "--hostname", "some-host", "--port", "1122") + Eventually(session.Err).Should(Say(`Incorrect Usage: The following arguments cannot be used together: --hostname, --port`)) + Eventually(session).Should(Exit(1)) + }) + }) + + When("--hostname and --random-port are provided", func() { + It("fails with a message about being unable to mix --random-port with any other options", func() { + session := helpers.CF("create-route", "some-space", "some-domain", "--hostname", "some-host", "--random-port") + Eventually(session.Err).Should(Say(`Incorrect Usage: The following arguments cannot be used together: --hostname, --random-port`)) + Eventually(session).Should(Exit(1)) + }) + }) + + When("--path and --port are provided", func() { + It("fails with a message about being unable to mix --port with the HTTP route options", func() { + session := helpers.CF("create-route", "some-space", "some-domain", "--path", "/some-path", "--port", "1111") + Eventually(session.Err).Should(Say(`Incorrect Usage: The following arguments cannot be used together: --path, --port`)) + Eventually(session).Should(Exit(1)) + }) + }) + + When("--path and --random-port are provided", func() { + It("fails with a message about being unable to mix --random-port with any other options", func() { + session := helpers.CF("create-route", "some-space", "some-domain", "--path", "/some-path", "--random-port") + Eventually(session.Err).Should(Say(`Incorrect Usage: The following arguments cannot be used together: --path, --random-port`)) + Eventually(session).Should(Exit(1)) + }) + }) + + When("both --port and --random-port are provided", func() { + It("fails with a message about being unable to mix --random-port with any other options", func() { + session := helpers.CF("create-route", "some-space", "some-domain", "--port", "1121", "--random-port") + Eventually(session.Err).Should(Say(`Incorrect Usage: The following arguments cannot be used together: --port, --random-port`)) + Eventually(session).Should(Exit(1)) + }) + }) + + When("the provided port is not valid / parseable", func() { + It("fails with an appropriate error", func() { + session := helpers.CF("create-route", "some-space", "some-domain", "--port", "ABC") + Eventually(session.Err).Should(Say(`Incorrect Usage: invalid argument for flag '--port' \(expected int > 0\)`)) + Eventually(session).Should(Exit(1)) + }) + }) + }) + + When("the environment is not setup correctly", func() { + It("fails with the appropriate errors", func() { + helpers.CheckEnvironmentTargetedCorrectly(true, false, ReadOnlyOrg, "create-route", "some-space", "some-domain") + }) + }) + + When("the environment is set up correctly", func() { + var ( + orgName string + spaceName string + ) + + BeforeEach(func() { + orgName = helpers.NewOrgName() + spaceName = helpers.NewSpaceName() + + helpers.SetupCF(orgName, spaceName) + }) + + AfterEach(func() { + helpers.QuickDeleteOrg(orgName) + }) + + When("the space does not exist", func() { + It("displays 'space not found' and exits 1", func() { + badSpaceName := fmt.Sprintf("%s-1", spaceName) + session := helpers.CF("create-route", badSpaceName, "some-domain") + Eventually(session).Should(Say(`FAILED`)) + Eventually(session.Err).Should(Say(`Space '%s' not found\.`, badSpaceName)) + Eventually(session).Should(Exit(1)) + }) + }) + + When("the space is not specified", func() { + It("displays error and exits 1", func() { + session := helpers.CF("create-route") + Eventually(session.Err).Should(Say("Incorrect Usage: the required arguments `SPACE` and `DOMAIN` were not provided\n")) + Eventually(session.Err).Should(Say("\n")) + Eventually(session).Should(Say("NAME:\n")) + Eventually(session).Should(Exit(1)) + }) + }) + + When("the domain does not exist", func() { + It("displays error and exits 1", func() { + session := helpers.CF("create-route", spaceName, "some-domain") + Eventually(session).Should(Say(`FAILED`)) + Eventually(session.Err).Should(Say(`Domain some-domain not found`)) + Eventually(session).Should(Exit(1)) + }) + }) + + When("the domain is not specified", func() { + It("displays error and exits 1", func() { + session := helpers.CF("create-route", spaceName) + Eventually(session.Err).Should(Say("Incorrect Usage: the required argument `DOMAIN` was not provided\n")) + Eventually(session.Err).Should(Say("\n")) + Eventually(session).Should(Say("NAME:\n")) + Eventually(session).Should(Exit(1)) + }) + }) + + When("the space and domain exist", func() { + var ( + userName string + domainName string + ) + + BeforeEach(func() { + domainName = helpers.NewDomainName() + userName, _ = helpers.GetCredentials() + }) + + When("the route already exists", func() { + var domain helpers.Domain + + BeforeEach(func() { + domain = helpers.NewDomain(orgName, domainName) + domain.Create() + Eventually(helpers.CF("create-route", spaceName, domainName)).Should(Exit(0)) + }) + + AfterEach(func() { + domain.Delete() + }) + + It("warns the user that it has already been created and runs to completion without failing", func() { + session := helpers.CF("create-route", spaceName, domainName) + Eventually(session).Should(Say(`Creating route %s for org %s / space %s as %s\.\.\.`, domainName, orgName, spaceName, userName)) + Eventually(session.Err).Should(Say(`Route %s already exists\.`, domainName)) + Eventually(session).Should(Say(`OK`)) + Eventually(session).Should(Exit(0)) + }) + }) + + When("the route already exists in a different space", func() { + var domain helpers.Domain + + BeforeEach(func() { + domain = helpers.NewDomain(orgName, domainName) + domain.Create() + differentSpaceName := helpers.NewSpaceName() + helpers.CreateSpace(differentSpaceName) + Eventually(helpers.CF("create-route", differentSpaceName, domainName)).Should(Exit(0)) + }) + + AfterEach(func() { + domain.Delete() + }) + + It("warns the user that the route is already in use and then fails", func() { + session := helpers.CF("create-route", spaceName, domainName) + Eventually(session).Should(Say(`Creating route %s for org %s / space %s as %s\.\.\.`, domainName, orgName, spaceName, userName)) + Eventually(session.Err).Should(Say("The app cannot be mapped to route %s because the route exists in a different space.", domainName)) + Eventually(session).Should(Say(`FAILED`)) + Eventually(session).Should(Exit(1)) + }) + }) + + When("the route does not already exist", func() { + When("the domain is private", func() { + var domain helpers.Domain + + BeforeEach(func() { + domain = helpers.NewDomain(orgName, domainName) + domain.Create() + Eventually(helpers.CF("create-route", spaceName, domainName)).Should(Exit(0)) + }) + + AfterEach(func() { + domain.Delete() + }) + + When("no flags are used", func() { + It("creates the route", func() { + session := helpers.CF("create-route", spaceName, domainName) + Eventually(session).Should(Say(`Creating route %s for org %s / space %s as %s\.\.\.`, domainName, orgName, spaceName, userName)) + Eventually(session).Should(Exit(0)) + }) + }) + + When("the path is provided but the hostname is not", func() { + var path string + + BeforeEach(func() { + path = helpers.PrefixedRandomName("path") + }) + + It("creates the route", func() { + session := helpers.CF("create-route", spaceName, domainName, "--path", path) + Eventually(session).Should(Say(`Creating route %s/%s for org %s / space %s as %s\.\.\.`, domainName, path, orgName, spaceName, userName)) + Eventually(session).Should(Exit(0)) + }) + }) + }) + + When("the domain is a shared HTTP domain", func() { + var domain helpers.Domain + + BeforeEach(func() { + domain = helpers.NewDomain(orgName, domainName) + domain.CreateShared() + }) + + AfterEach(func() { + domain.DeleteShared() + }) + + When("no flags are used", func() { + When("the domain already has some routes", func() { + var hostName string + + BeforeEach(func() { + hostName = helpers.PrefixedRandomName("my-host") + Eventually(helpers.CF("create-route", spaceName, domainName, "--hostname", hostName)).Should(Exit(0)) + }) + + It("fails with error message informing users to provide a port or random-port", func() { + session := helpers.CF("create-route", spaceName, domainName) + Eventually(session).Should(Say(`Creating route %s for org %s / space %s as %s\.\.\.`, domainName, orgName, spaceName, userName)) + Eventually(session).Should(Say(`FAILED`)) + Eventually(session.Err).Should(Say(`The route is invalid: host is required for shared-domains`)) + Eventually(session).Should(Exit(1)) + }) + }) + + It("fails with an error message and exits 1", func() { + session := helpers.CF("create-route", spaceName, domainName) + Eventually(session).Should(Say(`Creating route %s for org %s / space %s as %s\.\.\.`, domainName, orgName, spaceName, userName)) + Eventually(session.Err).Should(Say(`The route is invalid: host is required for shared-domains`)) + Eventually(session).Should(Exit(1)) + }) + }) + + When("TCP flag options are provided", func() { + It("fails with an error message and exits 1", func() { + port := "90230" + session := helpers.CF("create-route", spaceName, domainName, "--port", port) + Eventually(session).Should(Say(`Creating route %s:%s for org %s / space %s as %s\.\.\.`, domainName, port, orgName, spaceName, userName)) + Eventually(session.Err).Should(Say(`Port not allowed in HTTP domain %s`, domainName)) + Eventually(session).Should(Exit(1)) + }) + + It("fails with an error message and exits 1", func() { + session := helpers.CF("create-route", spaceName, domainName, "--random-port") + Eventually(session).Should(Say(`Creating route %s for org %s / space %s as %s\.\.\.`, domainName, orgName, spaceName, userName)) + Eventually(session.Err).Should(Say(`Port not allowed in HTTP domain %s`, domainName)) + Eventually(session).Should(Exit(1)) + }) + }) + + When("the hostname is provided", func() { + var hostName string + + BeforeEach(func() { + hostName = helpers.PrefixedRandomName("my-host") + }) + + When("no path is provided", func() { + It("creates the route", func() { + session := helpers.CF("create-route", spaceName, domainName, "--hostname", hostName) + Eventually(session).Should(Say(`Creating route %s.%s for org %s / space %s as %s\.\.\.`, hostName, domainName, orgName, spaceName, userName)) + Eventually(session).Should(Exit(0)) + }) + }) + + When("a path is provided", func() { + It("creates the route", func() { + path := fmt.Sprintf("/%s", helpers.PrefixedRandomName("path")) + session := helpers.CF("create-route", spaceName, domainName, "--hostname", hostName, "--path", path) + Eventually(session).Should(Say(`Creating route %s.%s%s for org %s / space %s as %s\.\.\.`, hostName, domainName, path, orgName, spaceName, userName)) + Eventually(session).Should(Exit(0)) + }) + }) + }) + + When("the hostname is not provided", func() { + var path string + + BeforeEach(func() { + path = helpers.PrefixedRandomName("path") + }) + + When("the path is provided", func() { + It("fails with an error message and exits 1", func() { + session := helpers.CF("create-route", spaceName, domainName, "--path", path) + Eventually(session).Should(Say(`Creating route %s/%s for org %s / space %s as %s\.\.\.`, domainName, path, orgName, spaceName, userName)) + Eventually(session.Err).Should(Say(`The route is invalid: host is required for shared-domains`)) + Eventually(session).Should(Exit(1)) + }) + }) + }) + }) + }) + }) + }) +}) diff --git a/integration/v7/isolated/verbose_flag_test.go b/integration/v7/isolated/verbose_flag_test.go new file mode 100644 index 00000000000..c00416f4950 --- /dev/null +++ b/integration/v7/isolated/verbose_flag_test.go @@ -0,0 +1,312 @@ +package isolated + +import ( + "io/ioutil" + "os" + "path/filepath" + "runtime" + "strings" + + "code.cloudfoundry.org/cli/integration/helpers" + "code.cloudfoundry.org/cli/util/configv3" + . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/extensions/table" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gbytes" + . "github.com/onsi/gomega/gexec" +) + +var _ = Describe("Verbose", func() { + DescribeTable("displays verbose output to terminal", + func(env string, configTrace string, flag bool) { + tmpDir, err := ioutil.TempDir("", "") + defer os.RemoveAll(tmpDir) + Expect(err).NotTo(HaveOccurred()) + + helpers.SetupCF(ReadOnlyOrg, ReadOnlySpace) + + // Invalidate the access token to cause a token refresh in order to + // test the call to the UAA. + helpers.SetConfig(func(config *configv3.Config) { + config.ConfigFile.AccessToken = helpers.InvalidAccessToken() + }) + + var envMap map[string]string + if env != "" { + if string(env[0]) == "/" { + env = filepath.Join(tmpDir, env) + } + envMap = map[string]string{"CF_TRACE": env} + } + + command := []string{"run-task", "app", "echo"} + + if flag { + command = append(command, "-v") + } + + if configTrace != "" { + if string(configTrace[0]) == "/" { + configTrace = filepath.Join(tmpDir, configTrace) + } + session := helpers.CF("config", "--trace", configTrace) + Eventually(session).Should(Exit(0)) + } + + session := helpers.CFWithEnv(envMap, command...) + + Eventually(session).Should(Say("REQUEST:")) + Eventually(session).Should(Say("GET /v3/apps")) + Eventually(session).Should(Say(`User-Agent: cf/[\w.+-]+ \(go\d+\.\d+(\.\d+)?; %s %s\)`, runtime.GOARCH, runtime.GOOS)) + Eventually(session).Should(Say("RESPONSE:")) + Eventually(session).Should(Say("REQUEST:")) + Eventually(session).Should(Say("POST /oauth/token")) + Eventually(session).Should(Say(`User-Agent: cf/[\w.+-]+ \(go\d+\.\d+(\.\d+)?; %s %s\)`, runtime.GOARCH, runtime.GOOS)) + Eventually(session).Should(Say(`\[PRIVATE DATA HIDDEN\]`)) //This is required to test the previous line. If it fails, the previous matcher went too far. + Eventually(session).Should(Say("RESPONSE:")) + Eventually(session).Should(Exit(1)) + }, + + Entry("CF_TRACE true: enables verbose", "true", "", false), + Entry("CF_Trace true, config trace false: enables verbose", "true", "false", false), + Entry("CF_Trace true, config trace file path: enables verbose AND logging to file", "true", "/foo", false), + + Entry("CF_TRACE false, '-v': enables verbose", "false", "", true), + Entry("CF_TRACE false, config trace file path, '-v': enables verbose AND logging to file", "false", "/foo", true), + + Entry("CF_TRACE empty:, '-v': enables verbose", "", "", true), + Entry("CF_TRACE empty, config trace true: enables verbose", "", "true", false), + Entry("CF_TRACE empty, config trace file path, '-v': enables verbose AND logging to file", "", "/foo", true), + + Entry("CF_TRACE filepath, '-v': enables logging to file", "/foo", "", true), + Entry("CF_TRACE filepath, config trace true: enables verbose AND logging to file", "/foo", "true", false), + Entry("CF_TRACE filepath, config trace filepath, '-v': enables verbose AND logging to file for BOTH paths", "/foo", "/bar", true), + ) + + DescribeTable("displays verbose output to multiple files", + func(env string, configTrace string, flag bool, location []string) { + tmpDir, err := ioutil.TempDir("", "") + defer os.RemoveAll(tmpDir) + Expect(err).NotTo(HaveOccurred()) + + helpers.SetupCF(ReadOnlyOrg, ReadOnlySpace) + + // Invalidate the access token to cause a token refresh in order to + // test the call to the UAA. + helpers.SetConfig(func(config *configv3.Config) { + config.ConfigFile.AccessToken = helpers.InvalidAccessToken() + }) + + var envMap map[string]string + if env != "" { + if string(env[0]) == "/" { + env = filepath.Join(tmpDir, env) + } + envMap = map[string]string{"CF_TRACE": env} + } + + command := []string{"run-task", "app", "echo"} + + if flag { + command = append(command, "-v") + } + + if configTrace != "" { + if string(configTrace[0]) == "/" { + configTrace = filepath.Join(tmpDir, configTrace) + } + session := helpers.CF("config", "--trace", configTrace) + Eventually(session).Should(Exit(0)) + } + + session := helpers.CFWithEnv(envMap, command...) + Eventually(session).Should(Exit(1)) + + for _, filePath := range location { + contents, err := ioutil.ReadFile(tmpDir + filePath) + Expect(err).ToNot(HaveOccurred()) + + Expect(string(contents)).To(MatchRegexp("REQUEST:")) + Expect(string(contents)).To(MatchRegexp("GET /v3/apps")) + Expect(string(contents)).To(MatchRegexp("RESPONSE:")) + Expect(string(contents)).To(MatchRegexp("REQUEST:")) + Expect(string(contents)).To(MatchRegexp("POST /oauth/token")) + Expect(string(contents)).To(MatchRegexp("RESPONSE:")) + + stat, err := os.Stat(tmpDir + filePath) + Expect(err).ToNot(HaveOccurred()) + + if runtime.GOOS == "windows" { + Expect(stat.Mode().String()).To(Equal(os.FileMode(0666).String())) + } else { + Expect(stat.Mode().String()).To(Equal(os.FileMode(0600).String())) + } + } + }, + + Entry("CF_Trace true, config trace file path: enables verbose AND logging to file", "true", "/foo", false, []string{"/foo"}), + + Entry("CF_TRACE false, config trace file path: enables logging to file", "false", "/foo", false, []string{"/foo"}), + Entry("CF_TRACE false, config trace file path, '-v': enables verbose AND logging to file", "false", "/foo", true, []string{"/foo"}), + + Entry("CF_TRACE empty, config trace file path: enables logging to file", "", "/foo", false, []string{"/foo"}), + Entry("CF_TRACE empty, config trace file path, '-v': enables verbose AND logging to file", "", "/foo", true, []string{"/foo"}), + + Entry("CF_TRACE filepath: enables logging to file", "/foo", "", false, []string{"/foo"}), + Entry("CF_TRACE filepath, '-v': enables logging to file", "/foo", "", true, []string{"/foo"}), + Entry("CF_TRACE filepath, config trace true: enables verbose AND logging to file", "/foo", "true", false, []string{"/foo"}), + Entry("CF_TRACE filepath, config trace filepath: enables logging to file for BOTH paths", "/foo", "/bar", false, []string{"/foo", "/bar"}), + Entry("CF_TRACE filepath, config trace filepath, '-v': enables verbose AND logging to file for BOTH paths", "/foo", "/bar", true, []string{"/foo", "/bar"}), + ) + + Describe("NOAA", func() { + var orgName string + + BeforeEach(func() { + orgName = helpers.NewOrgName() + spaceName := helpers.NewSpaceName() + + helpers.SetupCF(orgName, spaceName) + }) + + AfterEach(func() { + Eventually(helpers.CF("config", "--trace", "false")).Should(Exit(0)) + helpers.QuickDeleteOrg(orgName) + }) + + DescribeTable("displays verbose output to terminal", + func(env string, configTrace string, flag bool) { + tmpDir, err := ioutil.TempDir("", "") + defer os.RemoveAll(tmpDir) + Expect(err).NotTo(HaveOccurred()) + + appName := helpers.PrefixedRandomName("app") + + helpers.WithHelloWorldApp(func(appDir string) { + Eventually(helpers.CF("push", appName, "--no-start", "-p", appDir, "-b", "staticfile_buildpack", "--no-route")).Should(Exit(0)) + }) + + var envMap map[string]string + if env != "" { + if string(env[0]) == "/" { + env = filepath.Join(tmpDir, env) + } + envMap = map[string]string{"CF_TRACE": env} + } + + command := []string{"logs", appName} + + if flag { + command = append(command, "-v") + } + + if configTrace != "" { + if string(configTrace[0]) == "/" { + configTrace = filepath.Join(tmpDir, configTrace) + } + session := helpers.CF("config", "--trace", configTrace) + Eventually(session).Should(Exit(0)) + } + + session := helpers.CFWithEnv(envMap, command...) + + Eventually(session).Should(Say("REQUEST:")) + Eventually(session).Should(Say("POST /oauth/token")) + Eventually(session).Should(Say(`\[PRIVATE DATA HIDDEN\]`)) + Eventually(session).Should(Say("WEBSOCKET REQUEST:")) + Eventually(session).Should(Say(`Authorization: \[PRIVATE DATA HIDDEN\]`)) + Eventually(session.Kill()).Should(Exit()) + }, + + Entry("CF_TRACE true: enables verbose", "true", "", false), + Entry("CF_Trace true, config trace false: enables verbose", "true", "false", false), + Entry("CF_Trace true, config trace file path: enables verbose AND logging to file", "true", "/foo", false), + + Entry("CF_TRACE false, '-v': enables verbose", "false", "", true), + Entry("CF_TRACE false, config trace file path, '-v': enables verbose AND logging to file", "false", "/foo", true), + + Entry("CF_TRACE empty:, '-v': enables verbose", "", "", true), + Entry("CF_TRACE empty, config trace true: enables verbose", "", "true", false), + Entry("CF_TRACE empty, config trace file path, '-v': enables verbose AND logging to file", "", "/foo", true), + + Entry("CF_TRACE filepath, '-v': enables logging to file", "/foo", "", true), + Entry("CF_TRACE filepath, config trace true: enables verbose AND logging to file", "/foo", "true", false), + Entry("CF_TRACE filepath, config trace filepath, '-v': enables verbose AND logging to file for BOTH paths", "/foo", "/bar", true), + ) + + DescribeTable("displays verbose output to multiple files", + func(env string, configTrace string, location []string) { + tmpDir, err := ioutil.TempDir("", "") + defer os.RemoveAll(tmpDir) + Expect(err).NotTo(HaveOccurred()) + + appName := helpers.PrefixedRandomName("app") + + helpers.WithHelloWorldApp(func(appDir string) { + Eventually(helpers.CF("push", appName, "--no-start", "-p", appDir, "-b", "staticfile_buildpack", "--no-route")).Should(Exit(0)) + }) + + var envMap map[string]string + if env != "" { + if string(env[0]) == "/" { + env = filepath.Join(tmpDir, env) + } + envMap = map[string]string{"CF_TRACE": env} + } + + if configTrace != "" { + if strings.HasPrefix(configTrace, "/") { + configTrace = filepath.Join(tmpDir, configTrace) + } + session := helpers.CF("config", "--trace", configTrace) + Eventually(session).Should(Exit(0)) + } + + session := helpers.CFWithEnv(envMap, "logs", "-v", appName) + + Eventually(session).Should(Say("WEBSOCKET RESPONSE")) + Eventually(session.Kill()).Should(Exit()) + + for _, filePath := range location { + contents, err := ioutil.ReadFile(tmpDir + filePath) + Expect(err).ToNot(HaveOccurred()) + + Expect(string(contents)).To(MatchRegexp("REQUEST:")) + Expect(string(contents)).To(MatchRegexp("POST /oauth/token")) + Expect(string(contents)).To(MatchRegexp(`\[PRIVATE DATA HIDDEN\]`)) + Expect(string(contents)).To(MatchRegexp("WEBSOCKET REQUEST:")) + Expect(string(contents)).To(MatchRegexp(`Authorization: \[PRIVATE DATA HIDDEN\]`)) + + stat, err := os.Stat(tmpDir + filePath) + Expect(err).ToNot(HaveOccurred()) + + if runtime.GOOS == "windows" { + Expect(stat.Mode().String()).To(Equal(os.FileMode(0666).String())) + } else { + Expect(stat.Mode().String()).To(Equal(os.FileMode(0600).String())) + } + } + }, + + Entry("CF_Trace true, config trace file path: enables verbose AND logging to file", "true", "/foo", []string{"/foo"}), + + Entry("CF_TRACE false, config trace file path: enables logging to file", "false", "/foo", []string{"/foo"}), + Entry("CF_TRACE false, config trace file path, '-v': enables verbose AND logging to file", "false", "/foo", []string{"/foo"}), + + Entry("CF_TRACE empty, config trace file path: enables logging to file", "", "/foo", []string{"/foo"}), + Entry("CF_TRACE empty, config trace file path, '-v': enables verbose AND logging to file", "", "/foo", []string{"/foo"}), + + Entry("CF_TRACE filepath: enables logging to file", "/foo", "", []string{"/foo"}), + Entry("CF_TRACE filepath, '-v': enables logging to file", "/foo", "", []string{"/foo"}), + Entry("CF_TRACE filepath, config trace true: enables verbose AND logging to file", "/foo", "true", []string{"/foo"}), + Entry("CF_TRACE filepath, config trace filepath: enables logging to file for BOTH paths", "/foo", "/bar", []string{"/foo", "/bar"}), + Entry("CF_TRACE filepath, config trace filepath, '-v': enables verbose AND logging to file for BOTH paths", "/foo", "/bar", []string{"/foo", "/bar"}), + ) + }) +}) + +func assertLogsWrittenToFile(filepath string, expected string) { + contents, err := ioutil.ReadFile(filepath) + Expect(err).ToNot(HaveOccurred()) + Expect(string(contents)).To(ContainSubstring(expected), "Logging to a file failed") +}