github.com/DaAlbrecht/cf-cli@v0.0.0-20231128151943-1fe19bb400b9/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 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 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 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 When("the server does not have a verified certificate", func() { 156 Context("skipSSLValidation is false", func() { 157 BeforeEach(func() { 158 if runtime.GOOS == "darwin" { 159 Skip("ssl verification is different on darwin") 160 } 161 server.AppendHandlers( 162 CombineHandlers( 163 VerifyRequest(http.MethodGet, "/list"), 164 ), 165 ) 166 167 connection = NewConnection(false, 0) 168 }) 169 170 It("returns a UnverifiedServerError", func() { 171 request, err := http.NewRequest(http.MethodGet, server.URL(), nil) 172 Expect(err).ToNot(HaveOccurred()) 173 174 var response Response 175 err = connection.Make(request, &response, nil) 176 Expect(err).To(MatchError(pluginerror.UnverifiedServerError{URL: server.URL()})) 177 }) 178 }) 179 }) 180 181 When("the server's certificate does not match the hostname", func() { 182 Context("skipSSLValidation is false", func() { 183 BeforeEach(func() { 184 if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { 185 Skip("ssl validation has a different order on windows/darwin, will not be returned properly") 186 } 187 server.AppendHandlers( 188 CombineHandlers( 189 VerifyRequest(http.MethodGet, "/"), 190 ), 191 ) 192 193 connection = NewConnection(false, 0) 194 }) 195 196 // loopback.cli.fun is a custom DNS record setup to point to 127.0.0.1 197 It("returns a SSLValidationHostnameError", func() { 198 altHostURL := strings.Replace(server.URL(), "127.0.0.1", "loopback.cli.fun", -1) 199 request, err := http.NewRequest(http.MethodGet, altHostURL, nil) 200 Expect(err).ToNot(HaveOccurred()) 201 202 var response Response 203 err = connection.Make(request, &response, nil) 204 Expect(err).To(MatchError(pluginerror.SSLValidationHostnameError{ 205 Message: "x509: certificate is valid for example.com, not loopback.cli.fun", 206 })) 207 }) 208 }) 209 }) 210 }) 211 212 Describe("4xx and 5xx response codes", func() { 213 When("any 4xx or 5xx response codes are encountered", func() { 214 var rawResponse string 215 216 BeforeEach(func() { 217 rawResponse = `{ 218 "error":"some error" 219 "description": "some error description", 220 }` 221 server.AppendHandlers( 222 CombineHandlers( 223 VerifyRequest(http.MethodGet, "/list"), 224 RespondWith(http.StatusTeapot, rawResponse), 225 ), 226 ) 227 }) 228 229 It("returns a RawHTTPStatusError", func() { 230 request, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/list", server.URL()), nil) 231 Expect(err).ToNot(HaveOccurred()) 232 233 var response Response 234 err = connection.Make(request, &response, nil) 235 Expect(err).To(MatchError(pluginerror.RawHTTPStatusError{ 236 Status: "418 I'm a teapot", 237 RawResponse: []byte(rawResponse), 238 })) 239 240 Expect(server.ReceivedRequests()).To(HaveLen(1)) 241 }) 242 }) 243 }) 244 }) 245 })