package api import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" "github.com/supabase/auth/internal/conf" "github.com/supabase/auth/internal/observability" ) func TestHandleResponseErrorWithHTTPError(t *testing.T) { examples := []struct { HTTPError *HTTPError APIVersion string ExpectedBody string }{ { HTTPError: badRequestError(ErrorCodeBadJSON, "Unable to parse JSON"), APIVersion: "", ExpectedBody: "{\"code\":400,\"error_code\":\"" + ErrorCodeBadJSON + "\",\"msg\":\"Unable to parse JSON\"}", }, { HTTPError: badRequestError(ErrorCodeBadJSON, "Unable to parse JSON"), APIVersion: "2023-12-31", ExpectedBody: "{\"code\":400,\"error_code\":\"" + ErrorCodeBadJSON + "\",\"msg\":\"Unable to parse JSON\"}", }, { HTTPError: badRequestError(ErrorCodeBadJSON, "Unable to parse JSON"), APIVersion: "2024-01-01", ExpectedBody: "{\"code\":\"" + ErrorCodeBadJSON + "\",\"message\":\"Unable to parse JSON\"}", }, { HTTPError: &HTTPError{ HTTPStatus: http.StatusBadRequest, Message: "Uncoded failure", }, APIVersion: "2024-01-01", ExpectedBody: "{\"code\":\"" + ErrorCodeUnknown + "\",\"message\":\"Uncoded failure\"}", }, { HTTPError: &HTTPError{ HTTPStatus: http.StatusInternalServerError, Message: "Unexpected failure", }, APIVersion: "2024-01-01", ExpectedBody: "{\"code\":\"" + ErrorCodeUnexpectedFailure + "\",\"message\":\"Unexpected failure\"}", }, } for _, example := range examples { rec := httptest.NewRecorder() req, err := http.NewRequest(http.MethodPost, "http://example.com", nil) require.NoError(t, err) if example.APIVersion != "" { req.Header.Set(APIVersionHeaderName, example.APIVersion) } HandleResponseError(example.HTTPError, rec, req) require.Equal(t, example.HTTPError.HTTPStatus, rec.Code) require.Equal(t, example.ExpectedBody, rec.Body.String()) } } func TestRecoverer(t *testing.T) { var logBuffer bytes.Buffer config, err := conf.LoadGlobal(apiTestConfig) require.NoError(t, err) require.NoError(t, observability.ConfigureLogging(&config.Logging)) // logrus should write to the buffer so we can check if the logs are output correctly logrus.SetOutput(&logBuffer) panicHandler := recoverer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { panic("test panic") })) w := httptest.NewRecorder() req, err := http.NewRequest(http.MethodPost, "http://example.com", nil) require.NoError(t, err) panicHandler.ServeHTTP(w, req) require.Equal(t, http.StatusInternalServerError, w.Code) var data HTTPError // panic should return an internal server error require.NoError(t, json.NewDecoder(w.Body).Decode(&data)) require.Equal(t, ErrorCodeUnexpectedFailure, data.ErrorCode) require.Equal(t, http.StatusInternalServerError, data.HTTPStatus) require.Equal(t, "Internal Server Error", data.Message) // panic should log the error message internally var logs map[string]interface{} require.NoError(t, json.NewDecoder(&logBuffer).Decode(&logs)) require.Equal(t, "request panicked", logs["msg"]) require.Equal(t, "test panic", logs["panic"]) require.NotEmpty(t, logs["stack"]) }