mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +00:00
client: improve test-coverage for error-responses
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
@@ -3,6 +3,7 @@ package client // import "github.com/docker/docker/client"
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"gotest.tools/v3/assert"
|
||||
@@ -92,6 +94,136 @@ func TestPlainTextError(t *testing.T) {
|
||||
assert.Check(t, is.ErrorType(err, errdefs.IsSystem))
|
||||
}
|
||||
|
||||
// TestResponseErrors tests handling of error responses returned by the API.
|
||||
// It includes test-cases for malformed and invalid error-responses, as well
|
||||
// as plain text errors for backwards compatibility with API versions <1.24.
|
||||
func TestResponseErrors(t *testing.T) {
|
||||
errorResponse, err := json.Marshal(&types.ErrorResponse{
|
||||
Message: "Some error occurred",
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
doc string
|
||||
apiVersion string
|
||||
contentType string
|
||||
response string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
// Valid (types.ErrorResponse) error, but not using a fixture, to validate current implementation..
|
||||
doc: "JSON error (non-fixture)",
|
||||
contentType: "application/json",
|
||||
response: string(errorResponse),
|
||||
expected: `Error response from daemon: Some error occurred`,
|
||||
},
|
||||
{
|
||||
// Valid (types.ErrorResponse) error.
|
||||
doc: "JSON error",
|
||||
contentType: "application/json",
|
||||
response: `{"message":"Some error occurred"}`,
|
||||
expected: `Error response from daemon: Some error occurred`,
|
||||
},
|
||||
{
|
||||
// Valid (types.ErrorResponse) error with additional fields.
|
||||
doc: "JSON error with extra fields",
|
||||
contentType: "application/json",
|
||||
response: `{"message":"Some error occurred", "other_field": "some other field that's not part of types.ErrorResponse"}`,
|
||||
expected: `Error response from daemon: Some error occurred`,
|
||||
},
|
||||
{
|
||||
// API versions before 1.24 did not support JSON errors, and return response as-is.
|
||||
doc: "JSON error on old API",
|
||||
apiVersion: "1.23",
|
||||
contentType: "application/json",
|
||||
response: `{"message":"Some error occurred"}`,
|
||||
expected: `Error response from daemon: {"message":"Some error occurred"}`,
|
||||
},
|
||||
{
|
||||
doc: "plain-text error",
|
||||
contentType: "text/plain",
|
||||
response: `Some error occurred`,
|
||||
expected: `Error response from daemon: Some error occurred`,
|
||||
},
|
||||
{
|
||||
// TODO(thaJeztah): consider returning (partial) raw response for these
|
||||
doc: "malformed JSON",
|
||||
contentType: "application/json",
|
||||
response: `{"message":"Some error occurred`,
|
||||
expected: `Error reading JSON: unexpected end of JSON input`,
|
||||
},
|
||||
{
|
||||
// Server response that's valid JSON, but not the expected (types.ErrorResponse) scheme
|
||||
// TODO(thaJeztah): consider returning (partial) raw response for these
|
||||
doc: "incorrect JSON scheme",
|
||||
contentType: "application/json",
|
||||
response: `{"error":"Some error occurred"}`,
|
||||
expected: `Error response from daemon: `,
|
||||
},
|
||||
{
|
||||
// TODO(thaJeztah): improve handling of such errors; we can return the generic "502 Bad Gateway" instead
|
||||
doc: "html error",
|
||||
contentType: "text/html",
|
||||
response: `<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>502 Bad Gateway</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Bad Gateway</h1>
|
||||
<p>The server was unable to complete your request. Please try again later.</p>
|
||||
<p>If this problem persists, please <a href="https://example.com/support">contact support</a>.</p>
|
||||
</body>
|
||||
</html>`,
|
||||
expected: `Error response from daemon: <!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>502 Bad Gateway</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Bad Gateway</h1>
|
||||
<p>The server was unable to complete your request. Please try again later.</p>
|
||||
<p>If this problem persists, please <a href="https://example.com/support">contact support</a>.</p>
|
||||
</body>
|
||||
</html>`,
|
||||
},
|
||||
{
|
||||
// TODO(thaJeztah): improve handling of these errors (JSON: invalid character '<' looking for beginning of value)
|
||||
doc: "html error masquerading as JSON",
|
||||
contentType: "application/json",
|
||||
response: `<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>502 Bad Gateway</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Bad Gateway</h1>
|
||||
<p>The server was unable to complete your request. Please try again later.</p>
|
||||
<p>If this problem persists, please <a href="https://example.com/support">contact support</a>.</p>
|
||||
</body>
|
||||
</html>`,
|
||||
expected: `Error reading JSON: invalid character '<' looking for beginning of value`,
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.doc, func(t *testing.T) {
|
||||
client := &Client{
|
||||
version: tc.apiVersion,
|
||||
client: newMockClient(func(req *http.Request) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusBadRequest,
|
||||
Header: http.Header{"Content-Type": []string{tc.contentType}},
|
||||
Body: io.NopCloser(bytes.NewReader([]byte(tc.response))),
|
||||
}, nil
|
||||
}),
|
||||
}
|
||||
_, err := client.Ping(context.Background())
|
||||
assert.Check(t, is.Error(err, tc.expected))
|
||||
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInfiniteError(t *testing.T) {
|
||||
infinitR := rand.New(rand.NewSource(42))
|
||||
client := &Client{
|
||||
|
||||
Reference in New Issue
Block a user