More tests and human review

This commit is contained in:
binwiederhier
2026-03-21 16:27:41 -04:00
parent 393f730d11
commit 1742302f83
7 changed files with 115 additions and 16 deletions
+5 -6
View File
@@ -71,7 +71,7 @@ func (c *Client) listMultipartUploads(ctx context.Context) ([]*multipartUpload,
keyMarker = result.NextKeyMarker
uploadIDMarker = result.NextUploadIDMarker
}
return nil, fmt.Errorf("s3: listMultipartUploads exceeded %d pages", maxPages)
return nil, fmt.Errorf("error listing multipart uploads, exceeded %d pages", maxPages)
}
// abortMultipartUpload cancels an in-progress multipart upload. Called on error to clean up.
@@ -122,10 +122,9 @@ func (c *Client) putObjectMultipart(ctx context.Context, key string, body io.Rea
}
if err == io.EOF || errors.Is(err, io.ErrUnexpectedEOF) {
break
}
if err != nil {
} else if err != nil {
c.abortMultipartUpload(ctx, key, uploadID)
return fmt.Errorf("s3: PutObject read: %w", err)
return fmt.Errorf("error uploading object %s, reading from client failed: %w", key, err)
}
}
@@ -172,7 +171,7 @@ func (c *Client) completeMultipartUpload(ctx context.Context, key, uploadID stri
log.Tag(tagS3Client).Debug("Completing multipart upload for object %s, %d parts", key, len(parts))
bodyBytes, err := xml.Marshal(&completeMultipartUploadRequest{Parts: parts})
if err != nil {
return fmt.Errorf("s3: CompleteMultipartUpload marshal: %w", err)
return fmt.Errorf("error marshalling complete multipart upload request: %w", err)
}
reqURL := fmt.Sprintf("%s?uploadId=%s", c.config.ObjectURL(key), url.QueryEscape(uploadID))
respBody, err := c.do(ctx, "CompleteMultipartUpload", http.MethodPost, reqURL, bodyBytes, nil)
@@ -180,7 +179,7 @@ func (c *Client) completeMultipartUpload(ctx context.Context, key, uploadID stri
return err
}
// Check if the response contains an error (S3 can return 200 with an error body)
var errResp ErrorResponse
var errResp errorResponse
if xml.Unmarshal(respBody, &errResp) == nil && errResp.Code != "" {
return &errResp
}
+2 -2
View File
@@ -456,7 +456,7 @@ func TestClient_GetObject_NotFound(t *testing.T) {
_, _, err := client.GetObject(context.Background(), "nonexistent")
require.Error(t, err)
var errResp *ErrorResponse
var errResp *errorResponse
require.ErrorAs(t, err, &errResp)
require.Equal(t, 404, errResp.StatusCode)
require.Equal(t, "NoSuchKey", errResp.Code)
@@ -799,7 +799,7 @@ func TestClient_RealBucket(t *testing.T) {
// Get after delete should fail
_, _, err = client.GetObject(ctx, key)
require.Error(t, err)
var errResp *ErrorResponse
var errResp *errorResponse
require.ErrorAs(t, err, &errResp)
require.Equal(t, 404, errResp.StatusCode)
})
+3 -3
View File
@@ -76,15 +76,15 @@ type Object struct {
LastModified time.Time
}
// ErrorResponse is returned when S3 responds with a non-2xx status code.
type ErrorResponse struct {
// errorResponse is returned when S3 responds with a non-2xx status code.
type errorResponse struct {
StatusCode int
Code string `xml:"Code"`
Message string `xml:"Message"`
Body string `xml:"-"` // raw response body
}
func (e *ErrorResponse) Error() string {
func (e *errorResponse) Error() string {
if e.Code != "" {
return fmt.Sprintf("s3: %s (HTTP %d): %s", e.Code, e.StatusCode, e.Message)
}
+2 -2
View File
@@ -84,7 +84,7 @@ func ParseURL(s3URL string) (*Config, error) {
}, nil
}
// parseError reads an S3 error response and returns an *ErrorResponse.
// parseError reads an S3 error response and returns an *errorResponse.
func parseError(resp *http.Response) error {
body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes))
if err != nil {
@@ -94,7 +94,7 @@ func parseError(resp *http.Response) error {
}
func parseErrorFromBytes(statusCode int, body []byte) error {
errResp := &ErrorResponse{
errResp := &errorResponse{
StatusCode: statusCode,
Body: string(body),
}
+2 -2
View File
@@ -163,7 +163,7 @@ func TestParseError_XMLResponse(t *testing.T) {
xmlBody := []byte(`<?xml version="1.0" encoding="UTF-8"?><Error><Code>NoSuchKey</Code><Message>The specified key does not exist.</Message></Error>`)
err := parseErrorFromBytes(404, xmlBody)
var errResp *ErrorResponse
var errResp *errorResponse
require.ErrorAs(t, err, &errResp)
require.Equal(t, 404, errResp.StatusCode)
require.Equal(t, "NoSuchKey", errResp.Code)
@@ -173,7 +173,7 @@ func TestParseError_XMLResponse(t *testing.T) {
func TestParseError_NonXMLResponse(t *testing.T) {
err := parseErrorFromBytes(500, []byte("internal server error"))
var errResp *ErrorResponse
var errResp *errorResponse
require.ErrorAs(t, err, &errResp)
require.Equal(t, 500, errResp.StatusCode)
require.Equal(t, "", errResp.Code) // XML parsing failed, no code