debian-forge-composer/internal/cloudapi/v2/errors_test.go
Lukas Zapletal a58a399008 v2: update errors test for new error type
The oapi-codegen update to v2.5.0 changed the Details field of the main
Error type to be an empty interface. This requires updates to the error
handling tests to accommodate the new structure.
2025-08-05 18:55:32 +02:00

204 lines
6.8 KiB
Go

package v2
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/require"
)
func TestHTTPErrorReturnsEchoHTTPError(t *testing.T) {
for _, se := range getServiceErrors() {
err := HTTPError(se.code)
echoError, ok := err.(*echo.HTTPError)
require.True(t, ok)
require.Equal(t, se.httpStatus, echoError.Code)
detailsError, ok := echoError.Message.(detailsError)
require.True(t, ok)
require.Equal(t, se.code, detailsError.errorCode)
}
}
func TestAPIError(t *testing.T) {
e := echo.New()
for _, svcErr := range getServiceErrors() {
ctx := e.NewContext(nil, nil)
ctx.Set("operationID", "test-operation-id")
se := svcErr // avoid G601
apiError := APIError(&se, ctx, nil)
require.Equal(t, fmt.Sprintf("/api/image-builder-composer/v2/errors/%d", se.code), apiError.Href)
require.Equal(t, fmt.Sprintf("%d", se.code), apiError.Id)
require.Equal(t, "Error", apiError.Kind)
require.Equal(t, fmt.Sprintf("IMAGE-BUILDER-COMPOSER-%d", se.code), apiError.Code)
require.Equal(t, "test-operation-id", apiError.OperationId)
require.Equal(t, se.reason, apiError.Reason)
}
}
func TestAPIErrorOperationID(t *testing.T) {
ctx := echo.New().NewContext(nil, nil)
apiError := APIError(find(ErrorUnauthenticated), ctx, nil)
require.Equal(t, "IMAGE-BUILDER-COMPOSER-10003", apiError.Code)
ctx.Set("operationID", 5)
apiError = APIError(find(ErrorUnauthenticated), ctx, nil)
require.Equal(t, "IMAGE-BUILDER-COMPOSER-10003", apiError.Code)
ctx.Set("operationID", "test-operation-id")
apiError = APIError(find(ErrorUnauthenticated), ctx, nil)
require.Equal(t, "IMAGE-BUILDER-COMPOSER-401", apiError.Code)
}
func TestAPIErrorList(t *testing.T) {
ctx := echo.New().NewContext(nil, nil)
ctx.Set("operationID", "test-operation-id")
// negative values return empty list
errs := APIErrorList(-10, -30, ctx)
require.Equal(t, 0, errs.Size)
require.Equal(t, 0, len(errs.Items))
errs = APIErrorList(0, -30, ctx)
require.Equal(t, 0, errs.Size)
require.Equal(t, 0, len(errs.Items))
errs = APIErrorList(-10, 0, ctx)
require.Equal(t, 0, errs.Size)
require.Equal(t, 0, len(errs.Items))
// all of them
errs = APIErrorList(0, 1000, ctx)
require.Equal(t, len(getServiceErrors()), errs.Size)
// some of them
errs = APIErrorList(0, 10, ctx)
require.Equal(t, 10, errs.Size)
require.Equal(t, len(getServiceErrors()), errs.Total)
require.Equal(t, 0, errs.Page)
require.Equal(t, "IMAGE-BUILDER-COMPOSER-401", errs.Items[0].Code)
errs = APIErrorList(1, 10, ctx)
require.Equal(t, 10, errs.Size)
require.Equal(t, len(getServiceErrors()), errs.Total)
require.Equal(t, 1, errs.Page)
require.Equal(t, "IMAGE-BUILDER-COMPOSER-11", errs.Items[0].Code)
// high page
errs = APIErrorList(1000, 1, ctx)
require.Equal(t, 0, errs.Size)
require.Equal(t, len(getServiceErrors()), errs.Total)
require.Equal(t, 1000, errs.Page)
// zero pagesize
errs = APIErrorList(1000, 0, ctx)
require.Equal(t, 0, errs.Size)
require.Equal(t, len(getServiceErrors()), errs.Total)
require.Equal(t, 1000, errs.Page)
}
func TestHTTPErrorHandler(t *testing.T) {
e := echo.New()
// HTTPError
{
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
c.Set("operationID", "opid")
HTTPErrorHandler(HTTPError(ErrorEnqueueingJob), c)
require.Equal(t, find(ErrorEnqueueingJob).httpStatus, rec.Code)
var apiErr Error
require.NoError(t, json.NewDecoder(rec.Body).Decode(&apiErr))
require.NotNil(t, apiErr)
require.Equal(t, "opid", apiErr.OperationId)
require.Equal(t, find(ErrorEnqueueingJob).reason, apiErr.Reason)
require.Empty(t, apiErr.Details)
}
// HTTPErrorWithInternal
{
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
c.Set("operationID", "opid")
err := fmt.Errorf("some more details")
HTTPErrorHandler(HTTPErrorWithInternal(ErrorEnqueueingJob, err), c)
require.Equal(t, find(ErrorEnqueueingJob).httpStatus, rec.Code)
var apiErr Error
require.NoError(t, json.NewDecoder(rec.Body).Decode(&apiErr))
require.NotNil(t, apiErr)
require.Equal(t, "opid", apiErr.OperationId)
require.Equal(t, find(ErrorEnqueueingJob).reason, apiErr.Reason)
require.Equal(t, err.Error(), apiErr.Details)
}
// HTTPErrorWithDetails
// internalErr gets ignored for explicit details
{
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
c.Set("operationID", "opid")
err := fmt.Errorf("some more details")
HTTPErrorHandler(HTTPErrorWithDetails(ErrorEnqueueingJob, err, "even more extra details"), c)
require.Equal(t, find(ErrorEnqueueingJob).httpStatus, rec.Code)
var apiErr Error
require.NoError(t, json.NewDecoder(rec.Body).Decode(&apiErr))
require.NotNil(t, apiErr)
require.Equal(t, "opid", apiErr.OperationId)
require.Equal(t, find(ErrorEnqueueingJob).reason, apiErr.Reason)
require.Equal(t, "even more extra details", apiErr.Details)
}
// echo.HTTPError
{
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
c.Set("operationID", "opid")
err := fmt.Errorf("some unexpected internal http error")
HTTPErrorHandler(echo.NewHTTPError(http.StatusInternalServerError, err), c)
require.Equal(t, find(ErrorUnspecified).httpStatus, rec.Code)
var apiErr Error
require.NoError(t, json.NewDecoder(rec.Body).Decode(&apiErr))
require.NotNil(t, apiErr)
require.Equal(t, "opid", apiErr.OperationId)
require.Equal(t, find(ErrorUnspecified).reason, apiErr.Reason)
require.Equal(t, "code=500, message=some unexpected internal http error", apiErr.Details)
}
// echo.HTTPError and internalErr is nil
{
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
c.Set("operationID", "opid")
HTTPErrorHandler(echo.NewHTTPError(http.StatusInternalServerError, nil), c)
require.Equal(t, find(ErrorUnspecified).httpStatus, rec.Code)
var apiErr Error
require.NoError(t, json.NewDecoder(rec.Body).Decode(&apiErr))
require.NotNil(t, apiErr)
require.Equal(t, "opid", apiErr.OperationId)
require.Equal(t, find(ErrorUnspecified).reason, apiErr.Reason)
require.Equal(t, "code=500, message=<nil>", apiErr.Details)
}
// plain error
{
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
c.Set("operationID", "opid")
err := fmt.Errorf("some unexpected internal error")
HTTPErrorHandler(err, c)
require.Equal(t, find(ErrorNotHTTPError).httpStatus, rec.Code)
var apiErr Error
require.NoError(t, json.NewDecoder(rec.Body).Decode(&apiErr))
require.NotNil(t, apiErr)
require.Equal(t, "opid", apiErr.OperationId)
require.Equal(t, find(ErrorNotHTTPError).reason, apiErr.Reason)
require.Equal(t, "some unexpected internal error", apiErr.Details)
}
}