github.com/loggregator/cli@v6.33.1-0.20180224010324-82334f081791+incompatible/api/plugin/plugin_connection_test.go (about) 1 package plugin_test 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "net/http" 8 "runtime" 9 "strings" 10 11 . "code.cloudfoundry.org/cli/api/plugin" 12 "code.cloudfoundry.org/cli/api/plugin/pluginerror" 13 "code.cloudfoundry.org/cli/api/plugin/pluginfakes" 14 . "github.com/onsi/ginkgo" 15 . "github.com/onsi/gomega" 16 . "github.com/onsi/gomega/ghttp" 17 ) 18 19 type DummyResponse struct { 20 Val1 string `json:"val1"` 21 Val2 int `json:"val2"` 22 Val3 interface{} `json:"val3,omitempty"` 23 } 24 25 var _ = Describe("Plugin Connection", func() { 26 var ( 27 connection *PluginConnection 28 fakeProxyReader *pluginfakes.FakeProxyReader 29 ) 30 31 BeforeEach(func() { 32 connection = NewConnection(true, 0) 33 fakeProxyReader = new(pluginfakes.FakeProxyReader) 34 35 fakeProxyReader.WrapStub = func(reader io.Reader) io.ReadCloser { 36 return ioutil.NopCloser(reader) 37 } 38 }) 39 40 Describe("Make", func() { 41 Describe("Data Unmarshalling", func() { 42 var ( 43 request *http.Request 44 responseBody string 45 ) 46 47 BeforeEach(func() { 48 responseBody = `{ 49 "val1":"2.59.0", 50 "val2":2, 51 "val3":1111111111111111111 52 }` 53 server.AppendHandlers( 54 CombineHandlers( 55 VerifyRequest(http.MethodGet, "/list", ""), 56 RespondWith(http.StatusOK, responseBody), 57 ), 58 ) 59 60 var err error 61 request, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%s/list", server.URL()), nil) 62 Expect(err).ToNot(HaveOccurred()) 63 }) 64 65 Context("when passed a response with a result set", func() { 66 It("unmarshals the data into a struct", func() { 67 var body DummyResponse 68 response := Response{ 69 Result: &body, 70 } 71 72 err := connection.Make(request, &response, fakeProxyReader) 73 Expect(err).NotTo(HaveOccurred()) 74 75 Expect(body.Val1).To(Equal("2.59.0")) 76 Expect(body.Val2).To(Equal(2)) 77 78 Expect(fakeProxyReader.StartCallCount()).To(Equal(1)) 79 Expect(fakeProxyReader.StartArgsForCall(0)).To(BeEquivalentTo(len(responseBody))) 80 81 Expect(fakeProxyReader.WrapCallCount()).To(Equal(1)) 82 83 Expect(fakeProxyReader.FinishCallCount()).To(Equal(1)) 84 }) 85 86 It("keeps numbers unmarshalled to interfaces as interfaces", func() { 87 var body DummyResponse 88 response := Response{ 89 Result: &body, 90 } 91 92 err := connection.Make(request, &response, nil) 93 Expect(err).NotTo(HaveOccurred()) 94 Expect(fmt.Sprint(body.Val3)).To(Equal("1111111111111111111")) 95 }) 96 }) 97 98 Context("when passed an empty response", func() { 99 It("skips the unmarshalling step", func() { 100 var response Response 101 err := connection.Make(request, &response, nil) 102 Expect(err).NotTo(HaveOccurred()) 103 Expect(response.Result).To(BeNil()) 104 }) 105 }) 106 }) 107 108 Describe("HTTP Response", func() { 109 var request *http.Request 110 111 BeforeEach(func() { 112 response := `{}` 113 server.AppendHandlers( 114 CombineHandlers( 115 VerifyRequest(http.MethodGet, "/list", ""), 116 RespondWith(http.StatusOK, response), 117 ), 118 ) 119 120 var err error 121 request, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%s/list", server.URL()), nil) 122 Expect(err).ToNot(HaveOccurred()) 123 }) 124 125 It("returns the status", func() { 126 response := Response{} 127 128 err := connection.Make(request, &response, nil) 129 Expect(err).NotTo(HaveOccurred()) 130 131 Expect(response.HTTPResponse.Status).To(Equal("200 OK")) 132 }) 133 }) 134 135 Describe("Request errors", func() { 136 Context("when the server does not exist", func() { 137 BeforeEach(func() { 138 connection = NewConnection(false, 0) 139 }) 140 141 It("returns a RequestError", func() { 142 request, err := http.NewRequest(http.MethodGet, "http://i.hope.this.doesnt.exist.com/list", nil) 143 Expect(err).ToNot(HaveOccurred()) 144 145 var response Response 146 err = connection.Make(request, &response, nil) 147 Expect(err).To(HaveOccurred()) 148 149 requestErr, ok := err.(pluginerror.RequestError) 150 Expect(ok).To(BeTrue()) 151 Expect(requestErr.Error()).To(MatchRegexp(".*http://i.hope.this.doesnt.exist.com/list.*")) 152 }) 153 }) 154 155 Context("when the server does not have a verified certificate", func() { 156 Context("skipSSLValidation is false", func() { 157 BeforeEach(func() { 158 server.AppendHandlers( 159 CombineHandlers( 160 VerifyRequest(http.MethodGet, "/list"), 161 ), 162 ) 163 164 connection = NewConnection(false, 0) 165 }) 166 167 It("returns a UnverifiedServerError", func() { 168 request, err := http.NewRequest(http.MethodGet, server.URL(), nil) 169 Expect(err).ToNot(HaveOccurred()) 170 171 var response Response 172 err = connection.Make(request, &response, nil) 173 Expect(err).To(MatchError(pluginerror.UnverifiedServerError{URL: server.URL()})) 174 }) 175 }) 176 }) 177 178 Context("when the server's certificate does not match the hostname", func() { 179 Context("skipSSLValidation is false", func() { 180 BeforeEach(func() { 181 if runtime.GOOS == "windows" { 182 Skip("ssl validation has a different order on windows, will not be returned properly") 183 } 184 server.AppendHandlers( 185 CombineHandlers( 186 VerifyRequest(http.MethodGet, "/"), 187 ), 188 ) 189 190 connection = NewConnection(false, 0) 191 }) 192 193 // loopback.cli.ci.cf-app.com is a custom DNS record setup to point to 127.0.0.1 194 It("returns a SSLValidationHostnameError", func() { 195 altHostURL := strings.Replace(server.URL(), "127.0.0.1", "loopback.cli.ci.cf-app.com", -1) 196 request, err := http.NewRequest(http.MethodGet, altHostURL, nil) 197 Expect(err).ToNot(HaveOccurred()) 198 199 var response Response 200 err = connection.Make(request, &response, nil) 201 Expect(err).To(MatchError(pluginerror.SSLValidationHostnameError{ 202 Message: "x509: certificate is valid for example.com, not loopback.cli.ci.cf-app.com", 203 })) 204 }) 205 }) 206 }) 207 }) 208 209 Describe("4xx and 5xx response codes", func() { 210 Context("when any 4xx or 5xx response codes are encountered", func() { 211 var rawResponse string 212 213 BeforeEach(func() { 214 rawResponse = `{ 215 "error":"some error" 216 "description": "some error description", 217 }` 218 server.AppendHandlers( 219 CombineHandlers( 220 VerifyRequest(http.MethodGet, "/list"), 221 RespondWith(http.StatusTeapot, rawResponse), 222 ), 223 ) 224 }) 225 226 It("returns a RawHTTPStatusError", func() { 227 request, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/list", server.URL()), nil) 228 Expect(err).ToNot(HaveOccurred()) 229 230 var response Response 231 err = connection.Make(request, &response, nil) 232 Expect(err).To(MatchError(pluginerror.RawHTTPStatusError{ 233 Status: "418 I'm a teapot", 234 RawResponse: []byte(rawResponse), 235 })) 236 237 Expect(server.ReceivedRequests()).To(HaveLen(1)) 238 }) 239 }) 240 }) 241 }) 242 })