241 lines
5.7 KiB
Go
241 lines
5.7 KiB
Go
package errors
|
|
|
|
import (
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestAPIError_Error(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
err *APIError
|
|
want string
|
|
}{
|
|
{
|
|
name: "with message",
|
|
err: &APIError{
|
|
StatusCode: 404,
|
|
Message: "Flow not found",
|
|
},
|
|
want: "prefect api error (status 404): Flow not found",
|
|
},
|
|
{
|
|
name: "without message",
|
|
err: &APIError{
|
|
StatusCode: 500,
|
|
},
|
|
want: "prefect api error (status 500)",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := tt.err.Error(); got != tt.want {
|
|
t.Errorf("Error() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAPIError_Checks(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
statusCode int
|
|
checkFuncs map[string]func(*APIError) bool
|
|
want map[string]bool
|
|
}{
|
|
{
|
|
name: "404 not found",
|
|
statusCode: 404,
|
|
checkFuncs: map[string]func(*APIError) bool{
|
|
"IsNotFound": (*APIError).IsNotFound,
|
|
"IsUnauthorized": (*APIError).IsUnauthorized,
|
|
"IsForbidden": (*APIError).IsForbidden,
|
|
"IsRateLimited": (*APIError).IsRateLimited,
|
|
"IsServerError": (*APIError).IsServerError,
|
|
},
|
|
want: map[string]bool{
|
|
"IsNotFound": true,
|
|
"IsUnauthorized": false,
|
|
"IsForbidden": false,
|
|
"IsRateLimited": false,
|
|
"IsServerError": false,
|
|
},
|
|
},
|
|
{
|
|
name: "401 unauthorized",
|
|
statusCode: 401,
|
|
checkFuncs: map[string]func(*APIError) bool{
|
|
"IsNotFound": (*APIError).IsNotFound,
|
|
"IsUnauthorized": (*APIError).IsUnauthorized,
|
|
"IsForbidden": (*APIError).IsForbidden,
|
|
"IsRateLimited": (*APIError).IsRateLimited,
|
|
"IsServerError": (*APIError).IsServerError,
|
|
},
|
|
want: map[string]bool{
|
|
"IsNotFound": false,
|
|
"IsUnauthorized": true,
|
|
"IsForbidden": false,
|
|
"IsRateLimited": false,
|
|
"IsServerError": false,
|
|
},
|
|
},
|
|
{
|
|
name: "500 server error",
|
|
statusCode: 500,
|
|
checkFuncs: map[string]func(*APIError) bool{
|
|
"IsNotFound": (*APIError).IsNotFound,
|
|
"IsUnauthorized": (*APIError).IsUnauthorized,
|
|
"IsForbidden": (*APIError).IsForbidden,
|
|
"IsRateLimited": (*APIError).IsRateLimited,
|
|
"IsServerError": (*APIError).IsServerError,
|
|
},
|
|
want: map[string]bool{
|
|
"IsNotFound": false,
|
|
"IsUnauthorized": false,
|
|
"IsForbidden": false,
|
|
"IsRateLimited": false,
|
|
"IsServerError": true,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := &APIError{StatusCode: tt.statusCode}
|
|
for checkName, checkFunc := range tt.checkFuncs {
|
|
if got := checkFunc(err); got != tt.want[checkName] {
|
|
t.Errorf("%s() = %v, want %v", checkName, got, tt.want[checkName])
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewAPIError(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
resp *http.Response
|
|
wantStatus int
|
|
wantMsg string
|
|
}{
|
|
{
|
|
name: "nil response",
|
|
resp: nil,
|
|
wantStatus: 0,
|
|
wantMsg: "nil response",
|
|
},
|
|
{
|
|
name: "404 with JSON body",
|
|
resp: &http.Response{
|
|
StatusCode: 404,
|
|
Body: io.NopCloser(strings.NewReader(`{"detail": "Flow not found"}`)),
|
|
Header: http.Header{},
|
|
},
|
|
wantStatus: 404,
|
|
wantMsg: "Flow not found",
|
|
},
|
|
{
|
|
name: "500 without body",
|
|
resp: &http.Response{
|
|
StatusCode: 500,
|
|
Body: io.NopCloser(strings.NewReader("")),
|
|
Header: http.Header{},
|
|
},
|
|
wantStatus: 500,
|
|
wantMsg: "Internal Server Error",
|
|
},
|
|
{
|
|
name: "with request ID",
|
|
resp: &http.Response{
|
|
StatusCode: 400,
|
|
Body: io.NopCloser(strings.NewReader(`{"detail": "Bad request"}`)),
|
|
Header: http.Header{
|
|
"X-Request-ID": []string{"req-123"},
|
|
},
|
|
},
|
|
wantStatus: 400,
|
|
wantMsg: "Bad request",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := NewAPIError(tt.resp)
|
|
apiErr, ok := err.(*APIError)
|
|
if !ok {
|
|
t.Fatalf("expected *APIError, got %T", err)
|
|
}
|
|
if apiErr.StatusCode != tt.wantStatus {
|
|
t.Errorf("StatusCode = %v, want %v", apiErr.StatusCode, tt.wantStatus)
|
|
}
|
|
if apiErr.Message != tt.wantMsg {
|
|
t.Errorf("Message = %v, want %v", apiErr.Message, tt.wantMsg)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewAPIError_Validation(t *testing.T) {
|
|
resp := &http.Response{
|
|
StatusCode: 422,
|
|
Body: io.NopCloser(strings.NewReader(`{
|
|
"detail": [
|
|
{
|
|
"loc": ["body", "name"],
|
|
"msg": "field required",
|
|
"type": "value_error.missing"
|
|
}
|
|
]
|
|
}`)),
|
|
Header: http.Header{},
|
|
}
|
|
|
|
err := NewAPIError(resp)
|
|
valErr, ok := err.(*ValidationError)
|
|
if !ok {
|
|
t.Fatalf("expected *ValidationError, got %T", err)
|
|
}
|
|
|
|
if valErr.StatusCode != 422 {
|
|
t.Errorf("StatusCode = %v, want 422", valErr.StatusCode)
|
|
}
|
|
|
|
if len(valErr.ValidationErrors) != 1 {
|
|
t.Fatalf("expected 1 validation error, got %d", len(valErr.ValidationErrors))
|
|
}
|
|
|
|
vd := valErr.ValidationErrors[0]
|
|
if len(vd.Loc) != 2 || vd.Loc[0] != "body" || vd.Loc[1] != "name" {
|
|
t.Errorf("Loc = %v, want [body name]", vd.Loc)
|
|
}
|
|
if vd.Msg != "field required" {
|
|
t.Errorf("Msg = %v, want 'field required'", vd.Msg)
|
|
}
|
|
if vd.Type != "value_error.missing" {
|
|
t.Errorf("Type = %v, want 'value_error.missing'", vd.Type)
|
|
}
|
|
}
|
|
|
|
func TestHelperFunctions(t *testing.T) {
|
|
notFoundErr := &APIError{StatusCode: 404}
|
|
unauthorizedErr := &APIError{StatusCode: 401}
|
|
serverErr := &APIError{StatusCode: 500}
|
|
valErr := &ValidationError{APIError: &APIError{StatusCode: 422}}
|
|
|
|
if !IsNotFound(notFoundErr) {
|
|
t.Error("IsNotFound should return true for 404 error")
|
|
}
|
|
if !IsUnauthorized(unauthorizedErr) {
|
|
t.Error("IsUnauthorized should return true for 401 error")
|
|
}
|
|
if !IsServerError(serverErr) {
|
|
t.Error("IsServerError should return true for 500 error")
|
|
}
|
|
if !IsValidationError(valErr) {
|
|
t.Error("IsValidationError should return true for validation error")
|
|
}
|
|
}
|