github.com/liamawhite/cli-with-i18n@v6.32.1-0.20171122084555-dede0a5c3448+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  	. "github.com/liamawhite/cli-with-i18n/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  		testUI   *UI
    22  		display  *RequestLoggerFileWriter
    23  		tmpdir   string
    24  		logFile1 string
    25  		logFile2 string
    26  	)
    27  
    28  	BeforeEach(func() {
    29  		testUI = 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 = testUI.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  Authorization: bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImtleS0xIiwidHlwIjoiSldUIn0.eyJqdGkiOiI3YzRmYWEyZjI5MmQ0MTQ5ODM5NGE3OTU0Y2E3ZWNlMCIsInN1YiI6IjIyMjNiM2IzLTE3ZTktNDJkNi1iNzQzLThjZjcyZWIwOWRlNSIsInNjb3BlIjpbInJvdXRpbmcucm91dGVyX2dyb3Vwcy5yZWFkIiwiY2xvdWRfY29udHJvbGxlci5yZWFkIiwicGFzc3dvcmQud3JpdGUiLCJjbG91ZF9jb250cm9sbGVyLndyaXRlIiwib3BlbmlkIiwicm91dGluZy5yb3V0ZXJfZ3JvdXBzLndyaXRlIiwiZG9wcGxlci5maXJlaG9zZSIsInNjaW0ud3JpdGUiLCJzY2ltLnJlYWQiLCJjbG91ZF9jb250cm9sbGVyLmFkbWluIiwidWFhLnVzZXIiXSwiY2xpZW50X2lkIjoiY2YiLCJjaWQiOiJjZiIsImF6cCI6ImNmIiwiZ3JhbnRfdHlwZSI6InBhc3N3b3JkIiwidXNlcl9pZCI6IjIyMjNiM2IzLTE3ZTktNDJkNi1iNzQzLThjZjcyZWIwOWRlNSIsIm9yaWdpbiI6InVhYSIsInVzZXJfbmFtZSI6ImFkbWluIiwiZW1haWwiOiJhZG1pbiIsInJldl9zaWciOiI4NDBiMDBhMyIsImlhdCI6MTQ5NjQyNTU5NiwiZXhwIjoxNDk2NDI2MTk2LCJpc3MiOiJodHRwczovL3VhYS5ib3NoLWxpdGUuY29tL29hdXRoL3Rva2VuIiwiemlkIjoidWFhIiwiYXVkIjpbInNjaW0iLCJjbG91ZF9jb250cm9sbGVyIiwicGFzc3dvcmQiLCJjZiIsInVhYSIsIm9wZW5pZCIsImRvcHBsZXIiLCJyb3V0aW5nLnJvdXRlcl9ncm91cHMiXX0.TFDmHviKcs-eeNoz79dVwOl-k_dHTdqHkyztont2qnBDchNSpWvR5Yba54MMG8uTUHM72YbCopxdyaLY-g8s5wJFGLaBocrDgqswUh3mQRvynQG6_zne1h_0oHXnm0U-ZPnTyV8qjtHUoLvks4GOuktXc6ZE3NriWODpKIU5WdMgEbvyhuTnUEn88rQnmGJbKvHOIilulb6avSkZfTEq1o8w4VLCeRDlVLNh5JzCUtGzLfImNb31ks_Wv6HuI8kFjQZ5PQiTYjlhkuDQOcNSaAyWxQ_7425hiA7x8omBgEr-uST7GsxLvgoHqQaDH0JSTgMmO_GaN_QD52JVuru9og
    91  Origin: wss://doppler.bosh-lite.com:443`
    92  				err := display.DisplayDump(dump)
    93  				Expect(err).ToNot(HaveOccurred())
    94  
    95  				err = display.Stop()
    96  				Expect(err).ToNot(HaveOccurred())
    97  
    98  				raw, err := ioutil.ReadFile(logFile1)
    99  				Expect(err).ToNot(HaveOccurred())
   100  				contents := string(raw)
   101  
   102  				Expect(contents).To(MatchRegexp("Connection: Upgrade"))
   103  				Expect(contents).To(MatchRegexp("Authorization: \\[PRIVATE DATA HIDDEN\\]"))
   104  				Expect(contents).To(MatchRegexp("Origin: wss://doppler.bosh-lite.com:443"))
   105  
   106  				raw, err = ioutil.ReadFile(logFile2)
   107  				Expect(err).ToNot(HaveOccurred())
   108  				contents = string(raw)
   109  
   110  				Expect(contents).To(MatchRegexp("Connection: Upgrade"))
   111  				Expect(contents).To(MatchRegexp("Authorization: \\[PRIVATE DATA HIDDEN\\]"))
   112  				Expect(contents).To(MatchRegexp("Origin: wss://doppler.bosh-lite.com:443"))
   113  			})
   114  		})
   115  
   116  		Describe("DisplayHeader", func() {
   117  			It("writes the header key and value", func() {
   118  				err := display.DisplayHeader("Header", "Value")
   119  				Expect(err).ToNot(HaveOccurred())
   120  
   121  				err = display.Stop()
   122  				Expect(err).ToNot(HaveOccurred())
   123  
   124  				contents, err := ioutil.ReadFile(logFile1)
   125  				Expect(err).ToNot(HaveOccurred())
   126  				Expect(string(contents)).To(Equal("Header: Value\n\n"))
   127  
   128  				contents, err = ioutil.ReadFile(logFile2)
   129  				Expect(err).ToNot(HaveOccurred())
   130  				Expect(string(contents)).To(Equal("Header: Value\n\n"))
   131  			})
   132  		})
   133  
   134  		Describe("DisplayHost", func() {
   135  			It("writes the host", func() {
   136  				err := display.DisplayHost("banana")
   137  				Expect(err).ToNot(HaveOccurred())
   138  
   139  				err = display.Stop()
   140  				Expect(err).ToNot(HaveOccurred())
   141  
   142  				contents, err := ioutil.ReadFile(logFile1)
   143  				Expect(err).ToNot(HaveOccurred())
   144  				Expect(string(contents)).To(Equal("Host: banana\n\n"))
   145  
   146  				contents, err = ioutil.ReadFile(logFile2)
   147  				Expect(err).ToNot(HaveOccurred())
   148  				Expect(string(contents)).To(Equal("Host: banana\n\n"))
   149  			})
   150  		})
   151  
   152  		Describe("DisplayJSONBody", func() {
   153  			Context("when provided well formed JSON", func() {
   154  				It("writes a formatted output", func() {
   155  					raw := `{"a":"b", "c":"d", "don't escape HTML":"<&>"}`
   156  					formatted := `{
   157    "a": "b",
   158    "c": "d",
   159    "don't escape HTML": "<&>"
   160  }
   161  
   162  ` // Additonal spaces required
   163  					err := display.DisplayJSONBody([]byte(raw))
   164  					Expect(err).ToNot(HaveOccurred())
   165  
   166  					err = display.Stop()
   167  					Expect(err).ToNot(HaveOccurred())
   168  
   169  					contents, err := ioutil.ReadFile(logFile1)
   170  					Expect(err).ToNot(HaveOccurred())
   171  					Expect(string(contents)).To(Equal(formatted))
   172  
   173  					contents, err = ioutil.ReadFile(logFile2)
   174  					Expect(err).ToNot(HaveOccurred())
   175  					Expect(string(contents)).To(Equal(formatted))
   176  				})
   177  			})
   178  
   179  			Context("when the body is empty", func() {
   180  				It("does not write the body", func() {
   181  					err := display.DisplayJSONBody(nil)
   182  					Expect(err).ToNot(HaveOccurred())
   183  
   184  					err = display.Stop()
   185  					Expect(err).ToNot(HaveOccurred())
   186  
   187  					contents, err := ioutil.ReadFile(logFile1)
   188  					Expect(err).ToNot(HaveOccurred())
   189  					Expect(string(contents)).To(Equal("\n"))
   190  
   191  					contents, err = ioutil.ReadFile(logFile2)
   192  					Expect(err).ToNot(HaveOccurred())
   193  					Expect(string(contents)).To(Equal("\n"))
   194  				})
   195  			})
   196  
   197  			Context("when provided malformed JSON", func() {
   198  				It("displays the raw body", func() {
   199  					raw := `[{"data":1, "banana": 2}]`
   200  					err := display.DisplayJSONBody([]byte(raw))
   201  					Expect(err).ToNot(HaveOccurred())
   202  
   203  					err = display.Stop()
   204  					Expect(err).ToNot(HaveOccurred())
   205  
   206  					contents, err := ioutil.ReadFile(logFile1)
   207  					Expect(err).ToNot(HaveOccurred())
   208  					Expect(string(contents)).To(Equal(raw + "\n\n"))
   209  
   210  					contents, err = ioutil.ReadFile(logFile2)
   211  					Expect(err).ToNot(HaveOccurred())
   212  					Expect(string(contents)).To(Equal(raw + "\n\n"))
   213  				})
   214  			})
   215  		})
   216  
   217  		Describe("DisplayMessage", func() {
   218  			It("writes the message", func() {
   219  				msg := "i am a message!!!!"
   220  				err := display.DisplayMessage(msg)
   221  				Expect(err).ToNot(HaveOccurred())
   222  
   223  				err = display.Stop()
   224  				Expect(err).ToNot(HaveOccurred())
   225  
   226  				contents, err := ioutil.ReadFile(logFile1)
   227  				Expect(err).ToNot(HaveOccurred())
   228  				Expect(string(contents)).To(ContainSubstring(msg))
   229  
   230  				contents, err = ioutil.ReadFile(logFile2)
   231  				Expect(err).ToNot(HaveOccurred())
   232  				Expect(string(contents)).To(ContainSubstring(msg))
   233  			})
   234  		})
   235  
   236  		Describe("DisplayRequestHeader", func() {
   237  			It("writes the method, uri and http protocol", func() {
   238  				err := display.DisplayRequestHeader("GET", "/v2/spaces/guid/summary", "HTTP/1.1")
   239  				Expect(err).ToNot(HaveOccurred())
   240  
   241  				err = display.Stop()
   242  				Expect(err).ToNot(HaveOccurred())
   243  
   244  				contents, err := ioutil.ReadFile(logFile1)
   245  				Expect(err).ToNot(HaveOccurred())
   246  				Expect(string(contents)).To(Equal("GET /v2/spaces/guid/summary HTTP/1.1\n\n"))
   247  
   248  				contents, err = ioutil.ReadFile(logFile2)
   249  				Expect(err).ToNot(HaveOccurred())
   250  				Expect(string(contents)).To(Equal("GET /v2/spaces/guid/summary HTTP/1.1\n\n"))
   251  			})
   252  		})
   253  
   254  		Describe("DisplayResponseHeader", func() {
   255  			It("writes the method, uri and http protocol", func() {
   256  				err := display.DisplayResponseHeader("HTTP/1.1", "200 OK")
   257  				Expect(err).ToNot(HaveOccurred())
   258  
   259  				err = display.Stop()
   260  				Expect(err).ToNot(HaveOccurred())
   261  
   262  				contents, err := ioutil.ReadFile(logFile1)
   263  				Expect(err).ToNot(HaveOccurred())
   264  				Expect(string(contents)).To(Equal("HTTP/1.1 200 OK\n\n"))
   265  
   266  				contents, err = ioutil.ReadFile(logFile2)
   267  				Expect(err).ToNot(HaveOccurred())
   268  				Expect(string(contents)).To(Equal("HTTP/1.1 200 OK\n\n"))
   269  			})
   270  		})
   271  
   272  		Describe("DisplayType", func() {
   273  			It("writes the passed type and time in localized ISO 8601", func() {
   274  				passedTime := time.Now()
   275  				err := display.DisplayType("banana", passedTime)
   276  				Expect(err).ToNot(HaveOccurred())
   277  
   278  				err = display.Stop()
   279  				Expect(err).ToNot(HaveOccurred())
   280  
   281  				contents, err := ioutil.ReadFile(logFile1)
   282  				Expect(err).ToNot(HaveOccurred())
   283  				Expect(string(contents)).To(Equal(fmt.Sprintf("banana: [%s]\n\n", passedTime.Format(time.RFC3339))))
   284  
   285  				contents, err = ioutil.ReadFile(logFile2)
   286  				Expect(err).ToNot(HaveOccurred())
   287  				Expect(string(contents)).To(Equal(fmt.Sprintf("banana: [%s]\n\n", passedTime.Format(time.RFC3339))))
   288  			})
   289  		})
   290  
   291  		Describe("HandleInternalError", func() {
   292  			It("sends error to standard error", func() {
   293  				err := errors.New("foobar")
   294  				display.HandleInternalError(err)
   295  				Expect(testUI.Err).To(Say("foobar"))
   296  				Expect(display.Stop()).NotTo(HaveOccurred())
   297  			})
   298  		})
   299  
   300  		Describe("Start and Stop", func() {
   301  			BeforeEach(func() {
   302  				// Cleanup old display output directory
   303  				Expect(display.Stop()).NotTo(HaveOccurred())
   304  				logFile1 = filepath.Join(tmpdir, "tmp_sub_dir", "tmpfile3")
   305  				logFile2 = filepath.Join(tmpdir, "tmp", "sub", "dir", ".", "tmpfile4")
   306  				display = testUI.RequestLoggerFileWriter([]string{logFile1, logFile2})
   307  			})
   308  
   309  			It("locks and then unlocks the mutex properly", func() { // and creates the intermediate dirs
   310  				c := make(chan bool)
   311  				go func() {
   312  					Expect(display.Start()).ToNot(HaveOccurred())
   313  					c <- true
   314  				}()
   315  				Eventually(c).Should(Receive())
   316  				Expect(display.Stop()).NotTo(HaveOccurred())
   317  			})
   318  		})
   319  	})
   320  
   321  	Describe("when the log file path is invalid", func() {
   322  		var pathName string
   323  
   324  		BeforeEach(func() {
   325  			var err error
   326  			tmpdir, err = ioutil.TempDir("", "request_logger")
   327  			Expect(err).ToNot(HaveOccurred())
   328  
   329  			pathName = filepath.Join(tmpdir, "foo")
   330  		})
   331  
   332  		AfterEach(func() {
   333  			Expect(display.Stop()).NotTo(HaveOccurred())
   334  			Expect(os.RemoveAll(tmpdir)).NotTo(HaveOccurred())
   335  		})
   336  
   337  		It("returns the os error when we unsuccessfully try to write to a file", func() {
   338  			Expect(os.Mkdir(pathName, os.ModeDir|os.ModePerm)).NotTo(HaveOccurred())
   339  			display = testUI.RequestLoggerFileWriter([]string{pathName})
   340  			err := display.Start()
   341  
   342  			Expect(err).To(MatchError(fmt.Sprintf("open %s: is a directory", pathName)))
   343  		})
   344  
   345  		It("returns the os error when the parent directory for the log file is in the root directory", func() {
   346  			file, err := os.OpenFile(pathName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
   347  			Expect(err).ToNot(HaveOccurred())
   348  			_, err = file.WriteString("hello world")
   349  			Expect(err).ToNot(HaveOccurred())
   350  			err = file.Close()
   351  			Expect(err).ToNot(HaveOccurred())
   352  
   353  			display = testUI.RequestLoggerFileWriter([]string{filepath.Join(pathName, "bar")})
   354  			err = display.Start()
   355  
   356  			pathName = strings.Replace(pathName, `\`, `\\`, -1)
   357  			Expect(err).To(MatchError(MatchRegexp(fmt.Sprintf("mkdir %s", pathName))))
   358  		})
   359  	})
   360  })