github.com/cloudfoundry/cli@v7.1.0+incompatible/util/ui/request_logger_file_writer_test.go (about)

     1  package ui_test
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  	"time"
    11  
    12  	. "code.cloudfoundry.org/cli/util/ui"
    13  
    14  	. "github.com/onsi/ginkgo"
    15  	. "github.com/onsi/gomega"
    16  	. "github.com/onsi/gomega/gbytes"
    17  )
    18  
    19  var _ = Describe("Request Logger File Writer", func() {
    20  	var (
    21  		ui       *UI
    22  		display  *RequestLoggerFileWriter
    23  		tmpdir   string
    24  		logFile1 string
    25  		logFile2 string
    26  	)
    27  
    28  	BeforeEach(func() {
    29  		ui = NewTestUI(NewBuffer(), NewBuffer(), NewBuffer())
    30  	})
    31  
    32  	Describe("Valid file paths", func() {
    33  		BeforeEach(func() {
    34  			var err error
    35  			tmpdir, err = ioutil.TempDir("", "request_logger")
    36  			Expect(err).ToNot(HaveOccurred())
    37  
    38  			logFile1 = filepath.Join(tmpdir, "tmp_sub_dir", "tmpfile1")
    39  			logFile2 = filepath.Join(tmpdir, "tmp", "sub", "dir", ".", "tmpfile2")
    40  			display = ui.RequestLoggerFileWriter([]string{logFile1, logFile2})
    41  			Expect(display.Start()).ToNot(HaveOccurred())
    42  		})
    43  
    44  		AfterEach(func() {
    45  			Expect(os.RemoveAll(tmpdir)).NotTo(HaveOccurred())
    46  		})
    47  
    48  		Describe("DisplayBody", func() {
    49  			It("writes the redacted value", func() {
    50  				err := display.DisplayBody([]byte("this is a body"))
    51  				Expect(err).ToNot(HaveOccurred())
    52  
    53  				err = display.Stop()
    54  				Expect(err).ToNot(HaveOccurred())
    55  
    56  				contents, err := ioutil.ReadFile(logFile1)
    57  				Expect(err).ToNot(HaveOccurred())
    58  				Expect(string(contents)).To(Equal(RedactedValue + "\n"))
    59  
    60  				contents, err = ioutil.ReadFile(logFile2)
    61  				Expect(err).ToNot(HaveOccurred())
    62  				Expect(string(contents)).To(Equal(RedactedValue + "\n"))
    63  			})
    64  		})
    65  
    66  		Describe("DisplayDump", func() {
    67  			It("creates the intermediate dirs and writes the dump to file", func() {
    68  				err := display.DisplayDump("this is a dump of stuff")
    69  				Expect(err).ToNot(HaveOccurred())
    70  
    71  				err = display.Stop()
    72  				Expect(err).ToNot(HaveOccurred())
    73  
    74  				contents, err := ioutil.ReadFile(logFile1)
    75  				Expect(err).ToNot(HaveOccurred())
    76  				Expect(string(contents)).To(Equal("this is a dump of stuff\n"))
    77  
    78  				contents, err = ioutil.ReadFile(logFile2)
    79  				Expect(err).ToNot(HaveOccurred())
    80  				Expect(string(contents)).To(Equal("this is a dump of stuff\n"))
    81  			})
    82  
    83  			It("redacts auth tokens", func() {
    84  				dump := `GET /apps/ce03a2e2-95c0-4f3b-abb9-32718d408c8b/stream HTTP/1.1
    85  Host: wss://doppler.bosh-lite.com:443
    86  Upgrade: websocket
    87  Connection: Upgrade
    88  Sec-WebSocket-Version: 13
    89  Sec-WebSocket-Key: [HIDDEN]
    90  Set-Cookie: please-redact-this
    91  Authorization: bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImtleS0xIiwidHlwIjoiSldUIn0.eyJqdGkiOiI3YzRmYWEyZjI5MmQ0MTQ5ODM5NGE3OTU0Y2E3ZWNlMCIsInN1YiI6IjIyMjNiM2IzLTE3ZTktNDJkNi1iNzQzLThjZjcyZWIwOWRlNSIsInNjb3BlIjpbInJvdXRpbmcucm91dGVyX2dyb3Vwcy5yZWFkIiwiY2xvdWRfY29udHJvbGxlci5yZWFkIiwicGFzc3dvcmQud3JpdGUiLCJjbG91ZF9jb250cm9sbGVyLndyaXRlIiwib3BlbmlkIiwicm91dGluZy5yb3V0ZXJfZ3JvdXBzLndyaXRlIiwiZG9wcGxlci5maXJlaG9zZSIsInNjaW0ud3JpdGUiLCJzY2ltLnJlYWQiLCJjbG91ZF9jb250cm9sbGVyLmFkbWluIiwidWFhLnVzZXIiXSwiY2xpZW50X2lkIjoiY2YiLCJjaWQiOiJjZiIsImF6cCI6ImNmIiwiZ3JhbnRfdHlwZSI6InBhc3N3b3JkIiwidXNlcl9pZCI6IjIyMjNiM2IzLTE3ZTktNDJkNi1iNzQzLThjZjcyZWIwOWRlNSIsIm9yaWdpbiI6InVhYSIsInVzZXJfbmFtZSI6ImFkbWluIiwiZW1haWwiOiJhZG1pbiIsInJldl9zaWciOiI4NDBiMDBhMyIsImlhdCI6MTQ5NjQyNTU5NiwiZXhwIjoxNDk2NDI2MTk2LCJpc3MiOiJodHRwczovL3VhYS5ib3NoLWxpdGUuY29tL29hdXRoL3Rva2VuIiwiemlkIjoidWFhIiwiYXVkIjpbInNjaW0iLCJjbG91ZF9jb250cm9sbGVyIiwicGFzc3dvcmQiLCJjZiIsInVhYSIsIm9wZW5pZCIsImRvcHBsZXIiLCJyb3V0aW5nLnJvdXRlcl9ncm91cHMiXX0.TFDmHviKcs-eeNoz79dVwOl-k_dHTdqHkyztont2qnBDchNSpWvR5Yba54MMG8uTUHM72YbCopxdyaLY-g8s5wJFGLaBocrDgqswUh3mQRvynQG6_zne1h_0oHXnm0U-ZPnTyV8qjtHUoLvks4GOuktXc6ZE3NriWODpKIU5WdMgEbvyhuTnUEn88rQnmGJbKvHOIilulb6avSkZfTEq1o8w4VLCeRDlVLNh5JzCUtGzLfImNb31ks_Wv6HuI8kFjQZ5PQiTYjlhkuDQOcNSaAyWxQ_7425hiA7x8omBgEr-uST7GsxLvgoHqQaDH0JSTgMmO_GaN_QD52JVuru9og
    92  Origin: wss://doppler.bosh-lite.com:443`
    93  				err := display.DisplayDump(dump)
    94  				Expect(err).ToNot(HaveOccurred())
    95  
    96  				err = display.Stop()
    97  				Expect(err).ToNot(HaveOccurred())
    98  
    99  				raw, err := ioutil.ReadFile(logFile1)
   100  				Expect(err).ToNot(HaveOccurred())
   101  				contents := string(raw)
   102  
   103  				Expect(contents).To(MatchRegexp("Connection: Upgrade"))
   104  				Expect(contents).To(MatchRegexp(`Set-Cookie: \[PRIVATE DATA HIDDEN\]`))
   105  				Expect(contents).To(MatchRegexp(`Authorization: \[PRIVATE DATA HIDDEN\]`))
   106  				Expect(contents).To(MatchRegexp("Origin: wss://doppler.bosh-lite.com:443"))
   107  
   108  				raw, err = ioutil.ReadFile(logFile2)
   109  				Expect(err).ToNot(HaveOccurred())
   110  				contents = string(raw)
   111  
   112  				Expect(contents).To(MatchRegexp("Connection: Upgrade"))
   113  				Expect(contents).To(MatchRegexp(`Authorization: \[PRIVATE DATA HIDDEN\]`))
   114  				Expect(contents).To(MatchRegexp("Origin: wss://doppler.bosh-lite.com:443"))
   115  			})
   116  		})
   117  
   118  		Describe("DisplayHeader", func() {
   119  			It("writes the header key and value", func() {
   120  				err := display.DisplayHeader("Header", "Value")
   121  				Expect(err).ToNot(HaveOccurred())
   122  
   123  				err = display.Stop()
   124  				Expect(err).ToNot(HaveOccurred())
   125  
   126  				contents, err := ioutil.ReadFile(logFile1)
   127  				Expect(err).ToNot(HaveOccurred())
   128  				Expect(string(contents)).To(Equal("Header: Value\n\n"))
   129  
   130  				contents, err = ioutil.ReadFile(logFile2)
   131  				Expect(err).ToNot(HaveOccurred())
   132  				Expect(string(contents)).To(Equal("Header: Value\n\n"))
   133  			})
   134  		})
   135  
   136  		Describe("DisplayHost", func() {
   137  			It("writes the host", func() {
   138  				err := display.DisplayHost("banana")
   139  				Expect(err).ToNot(HaveOccurred())
   140  
   141  				err = display.Stop()
   142  				Expect(err).ToNot(HaveOccurred())
   143  
   144  				contents, err := ioutil.ReadFile(logFile1)
   145  				Expect(err).ToNot(HaveOccurred())
   146  				Expect(string(contents)).To(Equal("Host: banana\n\n"))
   147  
   148  				contents, err = ioutil.ReadFile(logFile2)
   149  				Expect(err).ToNot(HaveOccurred())
   150  				Expect(string(contents)).To(Equal("Host: banana\n\n"))
   151  			})
   152  		})
   153  
   154  		Describe("DisplayJSONBody", func() {
   155  			When("provided well formed JSON", func() {
   156  				It("writes a formatted output", func() {
   157  					raw := `{"a":"b", "c":"d", "don't escape HTML":"<&>"}`
   158  					formatted := `{
   159    "a": "b",
   160    "c": "d",
   161    "don't escape HTML": "<&>"
   162  }
   163  
   164  ` // Additional spaces required
   165  					err := display.DisplayJSONBody([]byte(raw))
   166  					Expect(err).ToNot(HaveOccurred())
   167  
   168  					err = display.Stop()
   169  					Expect(err).ToNot(HaveOccurred())
   170  
   171  					contents, err := ioutil.ReadFile(logFile1)
   172  					Expect(err).ToNot(HaveOccurred())
   173  					Expect(string(contents)).To(Equal(formatted))
   174  
   175  					contents, err = ioutil.ReadFile(logFile2)
   176  					Expect(err).ToNot(HaveOccurred())
   177  					Expect(string(contents)).To(Equal(formatted))
   178  				})
   179  			})
   180  
   181  			When("the body is empty", func() {
   182  				It("does not write the body", func() {
   183  					err := display.DisplayJSONBody(nil)
   184  					Expect(err).ToNot(HaveOccurred())
   185  
   186  					err = display.Stop()
   187  					Expect(err).ToNot(HaveOccurred())
   188  
   189  					contents, err := ioutil.ReadFile(logFile1)
   190  					Expect(err).ToNot(HaveOccurred())
   191  					Expect(string(contents)).To(Equal("\n"))
   192  
   193  					contents, err = ioutil.ReadFile(logFile2)
   194  					Expect(err).ToNot(HaveOccurred())
   195  					Expect(string(contents)).To(Equal("\n"))
   196  				})
   197  			})
   198  
   199  			When("provided malformed JSON", func() {
   200  				It("displays the raw body", func() {
   201  					raw := `[{"data":1, "banana": 2}`
   202  					err := display.DisplayJSONBody([]byte(raw))
   203  					Expect(err).ToNot(HaveOccurred())
   204  
   205  					err = display.Stop()
   206  					Expect(err).ToNot(HaveOccurred())
   207  
   208  					contents, err := ioutil.ReadFile(logFile1)
   209  					Expect(err).ToNot(HaveOccurred())
   210  					Expect(string(contents)).To(Equal(raw + "\n\n"))
   211  
   212  					contents, err = ioutil.ReadFile(logFile2)
   213  					Expect(err).ToNot(HaveOccurred())
   214  					Expect(string(contents)).To(Equal(raw + "\n\n"))
   215  				})
   216  			})
   217  		})
   218  
   219  		Describe("DisplayMessage", func() {
   220  			It("writes the message", func() {
   221  				msg := "i am a message!!!!"
   222  				err := display.DisplayMessage(msg)
   223  				Expect(err).ToNot(HaveOccurred())
   224  
   225  				err = display.Stop()
   226  				Expect(err).ToNot(HaveOccurred())
   227  
   228  				contents, err := ioutil.ReadFile(logFile1)
   229  				Expect(err).ToNot(HaveOccurred())
   230  				Expect(string(contents)).To(ContainSubstring(msg))
   231  
   232  				contents, err = ioutil.ReadFile(logFile2)
   233  				Expect(err).ToNot(HaveOccurred())
   234  				Expect(string(contents)).To(ContainSubstring(msg))
   235  			})
   236  		})
   237  
   238  		Describe("DisplayRequestHeader", func() {
   239  			It("writes the method, uri and http protocol", func() {
   240  				err := display.DisplayRequestHeader("GET", "/v2/spaces/guid/summary", "HTTP/1.1")
   241  				Expect(err).ToNot(HaveOccurred())
   242  
   243  				err = display.Stop()
   244  				Expect(err).ToNot(HaveOccurred())
   245  
   246  				contents, err := ioutil.ReadFile(logFile1)
   247  				Expect(err).ToNot(HaveOccurred())
   248  				Expect(string(contents)).To(Equal("GET /v2/spaces/guid/summary HTTP/1.1\n\n"))
   249  
   250  				contents, err = ioutil.ReadFile(logFile2)
   251  				Expect(err).ToNot(HaveOccurred())
   252  				Expect(string(contents)).To(Equal("GET /v2/spaces/guid/summary HTTP/1.1\n\n"))
   253  			})
   254  		})
   255  
   256  		Describe("DisplayResponseHeader", func() {
   257  			It("writes the method, uri and http protocol", func() {
   258  				err := display.DisplayResponseHeader("HTTP/1.1", "200 OK")
   259  				Expect(err).ToNot(HaveOccurred())
   260  
   261  				err = display.Stop()
   262  				Expect(err).ToNot(HaveOccurred())
   263  
   264  				contents, err := ioutil.ReadFile(logFile1)
   265  				Expect(err).ToNot(HaveOccurred())
   266  				Expect(string(contents)).To(Equal("HTTP/1.1 200 OK\n\n"))
   267  
   268  				contents, err = ioutil.ReadFile(logFile2)
   269  				Expect(err).ToNot(HaveOccurred())
   270  				Expect(string(contents)).To(Equal("HTTP/1.1 200 OK\n\n"))
   271  			})
   272  		})
   273  
   274  		Describe("DisplayType", func() {
   275  			It("writes the passed type and time in localized ISO 8601", func() {
   276  				passedTime := time.Now()
   277  				err := display.DisplayType("banana", passedTime)
   278  				Expect(err).ToNot(HaveOccurred())
   279  
   280  				err = display.Stop()
   281  				Expect(err).ToNot(HaveOccurred())
   282  
   283  				contents, err := ioutil.ReadFile(logFile1)
   284  				Expect(err).ToNot(HaveOccurred())
   285  				Expect(string(contents)).To(Equal(fmt.Sprintf("banana: [%s]\n\n", passedTime.Format(time.RFC3339))))
   286  
   287  				contents, err = ioutil.ReadFile(logFile2)
   288  				Expect(err).ToNot(HaveOccurred())
   289  				Expect(string(contents)).To(Equal(fmt.Sprintf("banana: [%s]\n\n", passedTime.Format(time.RFC3339))))
   290  			})
   291  		})
   292  
   293  		Describe("HandleInternalError", func() {
   294  			It("sends error to standard error", func() {
   295  				err := errors.New("foobar")
   296  				display.HandleInternalError(err)
   297  				Expect(ui.Err).To(Say("foobar"))
   298  				Expect(display.Stop()).NotTo(HaveOccurred())
   299  			})
   300  		})
   301  
   302  		Describe("Start and Stop", func() {
   303  			BeforeEach(func() {
   304  				// Cleanup old display output directory
   305  				Expect(display.Stop()).NotTo(HaveOccurred())
   306  				logFile1 = filepath.Join(tmpdir, "tmp_sub_dir", "tmpfile3")
   307  				logFile2 = filepath.Join(tmpdir, "tmp", "sub", "dir", ".", "tmpfile4")
   308  				display = ui.RequestLoggerFileWriter([]string{logFile1, logFile2})
   309  			})
   310  
   311  			It("locks and then unlocks the mutex properly", func() { // and creates the intermediate dirs
   312  				c := make(chan bool)
   313  				go func() {
   314  					Expect(display.Start()).ToNot(HaveOccurred())
   315  					c <- true
   316  				}()
   317  				Eventually(c).Should(Receive())
   318  				Expect(display.Stop()).NotTo(HaveOccurred())
   319  			})
   320  		})
   321  	})
   322  
   323  	Describe("when the log file path is invalid", func() {
   324  		var pathName string
   325  
   326  		BeforeEach(func() {
   327  			var err error
   328  			tmpdir, err = ioutil.TempDir("", "request_logger")
   329  			Expect(err).ToNot(HaveOccurred())
   330  
   331  			pathName = filepath.Join(tmpdir, "foo")
   332  		})
   333  
   334  		AfterEach(func() {
   335  			Expect(display.Stop()).NotTo(HaveOccurred())
   336  			Expect(os.RemoveAll(tmpdir)).NotTo(HaveOccurred())
   337  		})
   338  
   339  		It("returns the os error when we unsuccessfully try to write to a file", func() {
   340  			Expect(os.Mkdir(pathName, os.ModeDir|os.ModePerm)).NotTo(HaveOccurred())
   341  			display = ui.RequestLoggerFileWriter([]string{pathName})
   342  			err := display.Start()
   343  
   344  			Expect(err).To(MatchError(fmt.Sprintf("open %s: is a directory", pathName)))
   345  		})
   346  
   347  		It("returns the os error when the parent directory for the log file is in the root directory", func() {
   348  			file, err := os.OpenFile(pathName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
   349  			Expect(err).ToNot(HaveOccurred())
   350  			_, err = file.WriteString("hello world")
   351  			Expect(err).ToNot(HaveOccurred())
   352  			err = file.Close()
   353  			Expect(err).ToNot(HaveOccurred())
   354  
   355  			display = ui.RequestLoggerFileWriter([]string{filepath.Join(pathName, "bar")})
   356  			err = display.Start()
   357  
   358  			pathName = strings.Replace(pathName, `\`, `\\`, -1)
   359  			Expect(err).To(MatchError(MatchRegexp(fmt.Sprintf("mkdir %s", pathName))))
   360  		})
   361  	})
   362  
   363  	Describe("UI", func() {
   364  		Describe("RequestLoggerFileWriter", func() {
   365  			It("returns a RequestLoggerFileWriter with the consistent filewriting mutex", func() {
   366  				logger1 := ui.RequestLoggerFileWriter(nil)
   367  				logger2 := ui.RequestLoggerFileWriter(nil)
   368  
   369  				c := make(chan bool)
   370  				err := logger1.Start()
   371  				Expect(err).ToNot(HaveOccurred())
   372  				go func() {
   373  					Expect(logger2.Start()).ToNot(HaveOccurred())
   374  					c <- true
   375  				}()
   376  				Consistently(c).ShouldNot(Receive())
   377  				Expect(logger1.Stop()).ToNot(HaveOccurred())
   378  				Eventually(c).Should(Receive())
   379  			})
   380  		})
   381  	})
   382  })