github.com/aavshr/aws-sdk-go@v1.41.3/service/s3/statusok_error_test.go (about) 1 //go:build go1.7 2 // +build go1.7 3 4 package s3_test 5 6 import ( 7 "fmt" 8 "io" 9 "io/ioutil" 10 "net/http" 11 "net/http/httptest" 12 "strconv" 13 "strings" 14 "testing" 15 "time" 16 17 "github.com/aavshr/aws-sdk-go/aws" 18 "github.com/aavshr/aws-sdk-go/aws/awserr" 19 "github.com/aavshr/aws-sdk-go/aws/corehandlers" 20 "github.com/aavshr/aws-sdk-go/aws/request" 21 "github.com/aavshr/aws-sdk-go/awstesting/unit" 22 "github.com/aavshr/aws-sdk-go/service/s3" 23 ) 24 25 const errMsg = `<Error><Code>ErrorCode</Code><Message>message body</Message><RequestId>requestID</RequestId><HostId>hostID=</HostId></Error>` 26 const xmlPreambleMsg = `<?xml version="1.0" encoding="UTF-8"?>` 27 28 var lastModifiedTime = time.Date(2009, 11, 23, 0, 0, 0, 0, time.UTC) 29 30 func TestCopyObjectNoError(t *testing.T) { 31 const successMsg = ` 32 <?xml version="1.0" encoding="UTF-8"?> 33 <CopyObjectResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><LastModified>2009-11-23T0:00:00Z</LastModified><ETag>"1da64c7f13d1e8dbeaea40b905fd586c"</ETag></CopyObjectResult>` 34 35 var responseBodyClosed bool 36 res, err := newCopyTestSvc(successMsg, &responseBodyClosed).CopyObject(&s3.CopyObjectInput{ 37 Bucket: aws.String("bucketname"), 38 CopySource: aws.String("bucketname/exists.txt"), 39 Key: aws.String("destination.txt"), 40 }) 41 42 if err != nil { 43 t.Fatalf("expected no error, but received %v", err) 44 } 45 if e, a := fmt.Sprintf(`%q`, "1da64c7f13d1e8dbeaea40b905fd586c"), *res.CopyObjectResult.ETag; e != a { 46 t.Errorf("expected %s, but received %s", e, a) 47 } 48 if e, a := lastModifiedTime, *res.CopyObjectResult.LastModified; !e.Equal(a) { 49 t.Errorf("expected %v, but received %v", e, a) 50 } 51 if !responseBodyClosed { 52 t.Error("http response body is not closed") 53 } 54 } 55 56 func TestCopyObjectError(t *testing.T) { 57 var responseBodyClosed bool 58 _, err := newCopyTestSvc(errMsg, &responseBodyClosed).CopyObject(&s3.CopyObjectInput{ 59 Bucket: aws.String("bucketname"), 60 CopySource: aws.String("bucketname/doesnotexist.txt"), 61 Key: aws.String("destination.txt"), 62 }) 63 64 if err == nil { 65 t.Error("expected error, but received none") 66 } 67 e := err.(awserr.Error) 68 69 if e, a := "ErrorCode", e.Code(); e != a { 70 t.Errorf("expected %s, but received %s", e, a) 71 } 72 if e, a := "message body", e.Message(); e != a { 73 t.Errorf("expected %s, but received %s", e, a) 74 } 75 if !responseBodyClosed { 76 t.Error("http response body is not closed") 77 } 78 } 79 80 func TestUploadPartCopySuccess(t *testing.T) { 81 const successMsg = ` 82 <?xml version="1.0" encoding="UTF-8"?> 83 <UploadPartCopyResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><LastModified>2009-11-23T0:00:00Z</LastModified><ETag>"1da64c7f13d1e8dbeaea40b905fd586c"</ETag></UploadPartCopyResult>` 84 85 var responseBodyClosed bool 86 res, err := newCopyTestSvc(successMsg, &responseBodyClosed).UploadPartCopy(&s3.UploadPartCopyInput{ 87 Bucket: aws.String("bucketname"), 88 CopySource: aws.String("bucketname/doesnotexist.txt"), 89 Key: aws.String("destination.txt"), 90 PartNumber: aws.Int64(0), 91 UploadId: aws.String("uploadID"), 92 }) 93 94 if err != nil { 95 t.Errorf("expected no error, but received %v", err) 96 } 97 98 if e, a := fmt.Sprintf(`%q`, "1da64c7f13d1e8dbeaea40b905fd586c"), *res.CopyPartResult.ETag; e != a { 99 t.Errorf("expected %s, but received %s", e, a) 100 } 101 if e, a := lastModifiedTime, *res.CopyPartResult.LastModified; !e.Equal(a) { 102 t.Errorf("expected %v, but received %v", e, a) 103 } 104 if !responseBodyClosed { 105 t.Error("http response body is not closed") 106 } 107 } 108 109 func TestUploadPartCopyError(t *testing.T) { 110 var responseBodyClosed bool 111 _, err := newCopyTestSvc(errMsg, &responseBodyClosed).UploadPartCopy(&s3.UploadPartCopyInput{ 112 Bucket: aws.String("bucketname"), 113 CopySource: aws.String("bucketname/doesnotexist.txt"), 114 Key: aws.String("destination.txt"), 115 PartNumber: aws.Int64(0), 116 UploadId: aws.String("uploadID"), 117 }) 118 119 if err == nil { 120 t.Error("expected an error") 121 } 122 e := err.(awserr.Error) 123 124 if e, a := "ErrorCode", e.Code(); e != a { 125 t.Errorf("expected %s, but received %s", e, a) 126 } 127 if e, a := "message body", e.Message(); e != a { 128 t.Errorf("expected %s, but received %s", e, a) 129 } 130 if !responseBodyClosed { 131 t.Error("http response body is not closed") 132 } 133 } 134 135 func TestCompleteMultipartUploadSuccess(t *testing.T) { 136 const successMsg = ` 137 <?xml version="1.0" encoding="UTF-8"?> 138 <CompleteMultipartUploadResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Location>locationName</Location><Bucket>bucketName</Bucket><Key>keyName</Key><ETag>"etagVal"</ETag></CompleteMultipartUploadResult>` 139 140 var responseBodyClosed bool 141 res, err := newCopyTestSvc(successMsg, &responseBodyClosed).CompleteMultipartUpload(&s3.CompleteMultipartUploadInput{ 142 Bucket: aws.String("bucketname"), 143 Key: aws.String("key"), 144 UploadId: aws.String("uploadID"), 145 }) 146 147 if err != nil { 148 t.Errorf("expected no error, but received %v", err) 149 } 150 151 if e, a := `"etagVal"`, *res.ETag; e != a { 152 t.Errorf("expected %s, but received %s", e, a) 153 } 154 if e, a := "bucketName", *res.Bucket; e != a { 155 t.Errorf("expected %s, but received %s", e, a) 156 } 157 if e, a := "keyName", *res.Key; e != a { 158 t.Errorf("expected %s, but received %s", e, a) 159 } 160 if e, a := "locationName", *res.Location; e != a { 161 t.Errorf("expected %s, but received %s", e, a) 162 } 163 if !responseBodyClosed { 164 t.Error("http response body is not closed") 165 } 166 } 167 168 func TestCompleteMultipartUploadError(t *testing.T) { 169 var responseBodyClosed bool 170 _, err := newCopyTestSvc(errMsg, &responseBodyClosed).CompleteMultipartUpload(&s3.CompleteMultipartUploadInput{ 171 Bucket: aws.String("bucketname"), 172 Key: aws.String("key"), 173 UploadId: aws.String("uploadID"), 174 }) 175 176 if err == nil { 177 t.Error("expected an error") 178 } 179 e := err.(awserr.Error) 180 181 if e, a := "ErrorCode", e.Code(); e != a { 182 t.Errorf("expected %s, but received %s", e, a) 183 } 184 if e, a := "message body", e.Message(); e != a { 185 t.Errorf("expected %s, but received %s", e, a) 186 } 187 if !responseBodyClosed { 188 t.Error("http response body is not closed") 189 } 190 } 191 192 func newCopyTestSvc(errMsg string, responseBodyClosed *bool) *s3.S3 { 193 const statusCode = http.StatusOK 194 195 svc := s3.New(unit.Session, &aws.Config{ 196 MaxRetries: aws.Int(0), 197 SleepDelay: func(time.Duration) {}, 198 }) 199 200 closeChecker := func() error { 201 if responseBodyClosed != nil { 202 *responseBodyClosed = true 203 } 204 return nil 205 } 206 207 svc.Handlers.Send.Swap(corehandlers.SendHandler.Name, 208 request.NamedHandler{ 209 Name: "newCopyTestSvc", 210 Fn: func(r *request.Request) { 211 io.Copy(ioutil.Discard, r.HTTPRequest.Body) 212 r.HTTPRequest.Body.Close() 213 r.HTTPResponse = &http.Response{ 214 Status: http.StatusText(statusCode), 215 StatusCode: statusCode, 216 Header: http.Header{}, 217 Body: newFuncCloser(strings.NewReader(errMsg), closeChecker), 218 } 219 }, 220 }) 221 222 return svc 223 } 224 225 // funcCloser converts io.Reader into io.ReadCloser by adding a custom function that is called on Close() 226 type funcCloser struct { 227 io.Reader 228 closeFn func() error 229 } 230 231 func newFuncCloser(reader io.Reader, closeFn func() error) *funcCloser { 232 return &funcCloser{Reader: reader, closeFn: closeFn} 233 } 234 235 func (f funcCloser) Close() error { 236 return f.closeFn() 237 } 238 239 func TestStatusOKPayloadHandling(t *testing.T) { 240 cases := map[string]struct { 241 Header http.Header 242 Payloads [][]byte 243 OpCall func(*s3.S3) error 244 Err string 245 }{ 246 "200 error": { 247 Header: http.Header{ 248 "Content-Length": []string{strconv.Itoa(len(errMsg))}, 249 }, 250 Payloads: [][]byte{[]byte(errMsg)}, 251 OpCall: func(c *s3.S3) error { 252 _, err := c.CopyObject(&s3.CopyObjectInput{ 253 Bucket: aws.String("bucketname"), 254 CopySource: aws.String("bucketname/doesnotexist.txt"), 255 Key: aws.String("destination.txt"), 256 }) 257 return err 258 }, 259 Err: "ErrorCode: message body", 260 }, 261 "200 error partial response": { 262 Header: http.Header{ 263 "Content-Length": []string{strconv.Itoa(len(errMsg))}, 264 }, 265 Payloads: [][]byte{ 266 []byte(errMsg[:20]), 267 }, 268 OpCall: func(c *s3.S3) error { 269 _, err := c.CopyObject(&s3.CopyObjectInput{ 270 Bucket: aws.String("bucketname"), 271 CopySource: aws.String("bucketname/doesnotexist.txt"), 272 Key: aws.String("destination.txt"), 273 }) 274 return err 275 }, 276 Err: "unexpected EOF", 277 }, 278 "200 error multipart": { 279 Header: http.Header{ 280 "Transfer-Encoding": []string{"chunked"}, 281 }, 282 Payloads: [][]byte{ 283 []byte(errMsg[:20]), 284 []byte(errMsg[20:]), 285 }, 286 OpCall: func(c *s3.S3) error { 287 _, err := c.CopyObject(&s3.CopyObjectInput{ 288 Bucket: aws.String("bucketname"), 289 CopySource: aws.String("bucketname/doesnotexist.txt"), 290 Key: aws.String("destination.txt"), 291 }) 292 return err 293 }, 294 Err: "ErrorCode: message body", 295 }, 296 "200 error multipart partial response": { 297 Header: http.Header{ 298 "Transfer-Encoding": []string{"chunked"}, 299 }, 300 Payloads: [][]byte{ 301 []byte(errMsg[:20]), 302 }, 303 OpCall: func(c *s3.S3) error { 304 _, err := c.CopyObject(&s3.CopyObjectInput{ 305 Bucket: aws.String("bucketname"), 306 CopySource: aws.String("bucketname/doesnotexist.txt"), 307 Key: aws.String("destination.txt"), 308 }) 309 return err 310 }, 311 Err: "XML syntax error", 312 }, 313 "200 error multipart no payload": { 314 Header: http.Header{ 315 "Transfer-Encoding": []string{"chunked"}, 316 }, 317 Payloads: [][]byte{}, 318 OpCall: func(c *s3.S3) error { 319 _, err := c.CopyObject(&s3.CopyObjectInput{ 320 Bucket: aws.String("bucketname"), 321 CopySource: aws.String("bucketname/doesnotexist.txt"), 322 Key: aws.String("destination.txt"), 323 }) 324 return err 325 }, 326 Err: "empty response payload", 327 }, 328 "response with only xml preamble": { 329 Header: http.Header{ 330 "Transfer-Encoding": []string{"chunked"}, 331 }, 332 Payloads: [][]byte{ 333 []byte(xmlPreambleMsg), 334 }, 335 OpCall: func(c *s3.S3) error { 336 _, err := c.CopyObject(&s3.CopyObjectInput{ 337 Bucket: aws.String("bucketname"), 338 CopySource: aws.String("bucketname/doesnotexist.txt"), 339 Key: aws.String("destination.txt"), 340 }) 341 return err 342 }, 343 Err: "empty response payload", 344 }, 345 } 346 347 for name, c := range cases { 348 t.Run(name, func(t *testing.T) { 349 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 350 ww := w.(interface { 351 http.ResponseWriter 352 http.Flusher 353 }) 354 355 for k, vs := range c.Header { 356 for _, v := range vs { 357 ww.Header().Add(k, v) 358 } 359 } 360 ww.WriteHeader(http.StatusOK) 361 ww.Flush() 362 363 for _, p := range c.Payloads { 364 ww.Write(p) 365 ww.Flush() 366 } 367 })) 368 defer srv.Close() 369 370 client := s3.New(unit.Session, &aws.Config{ 371 Endpoint: &srv.URL, 372 DisableSSL: aws.Bool(true), 373 DisableParamValidation: aws.Bool(true), 374 S3ForcePathStyle: aws.Bool(true), 375 }) 376 377 err := c.OpCall(client) 378 if len(c.Err) != 0 { 379 if err == nil { 380 t.Fatalf("expect error, got none") 381 } 382 if e, a := c.Err, err.Error(); !strings.Contains(a, e) { 383 t.Fatalf("expect %v error in, %v", e, a) 384 } 385 } else if err != nil { 386 t.Fatalf("expect no error, got %v", err) 387 } 388 }) 389 } 390 }